未验证 提交 bc7919c3 编写于 作者: 羽飞's avatar 羽飞 提交者: GitHub

Train debug (#203)

### What problem were solved in this pull request?

Problem:
与训练营配合,可以在SQL命令请求过程中,添加调试信息

### What is changed and how it works?
可以参考 debug-output.md 文档。
增加 sql_debug 变量,使用set sql_debug=1; 可以设置。
在SQL命令执行过程中,调用sql_debug函数,增加调试信息。在普通文本通讯协议中,调试信息会以 '#'
开头的形式打印出来。但是注意调试信息中不要带换行符。

### Other information
上级 99da0457
......@@ -9,7 +9,7 @@
"request": "launch",
"name": "Debug",
"program": "${workspaceFolder}/${defaultBuildTask}/bin/observer",
"args": ["-f", "${workspaceFolder}/etc/observer.ini", "-s", "miniob.sock"],
"args": ["-f", "${workspaceFolder}/etc/observer.ini", "-P", "cli"],
"cwd": "${workspaceFolder}/${defaultBuildTask}/"
}
]
......
......@@ -5,7 +5,7 @@ TOPDIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
BUILD_SH=$TOPDIR/build.sh
CMAKE_COMMAND="cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=1"
CMAKE_COMMAND="cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=1 --log-level=WARNING"
ALL_ARGS=("$@")
BUILD_ARGS=()
......@@ -73,38 +73,40 @@ function do_init
git submodule update --init || return
current_dir=$PWD
MAKE_COMMAND="make --silent"
# build libevent
cd ${TOPDIR}/deps/3rd/libevent && \
git checkout release-2.1.12-stable && \
mkdir -p build && \
cd build && \
cmake .. -DEVENT__DISABLE_OPENSSL=ON -DEVENT__LIBRARY_TYPE=BOTH && \
make -j4 && \
${CMAKE_COMMAND} .. -DEVENT__DISABLE_OPENSSL=ON -DEVENT__LIBRARY_TYPE=BOTH && \
${MAKE_COMMAND} -j4 && \
make install
# build googletest
cd ${TOPDIR}/deps/3rd/googletest && \
mkdir -p build && \
cd build && \
cmake .. && \
make -j4 && \
make install
${CMAKE_COMMAND} .. && \
${MAKE_COMMAND} -j4 && \
${MAKE_COMMAND} install
# build google benchmark
cd ${TOPDIR}/deps/3rd/benchmark && \
mkdir -p build && \
cd build && \
cmake .. -DBENCHMARK_ENABLE_TESTING=OFF -DBENCHMARK_INSTALL_DOCS=OFF -DBENCHMARK_ENABLE_GTEST_TESTS=OFF -DBENCHMARK_USE_BUNDLED_GTEST=OFF -DBENCHMARK_ENABLE_ASSEMBLY_TESTS=OFF && \
make -j4 && \
make install
${CMAKE_COMMAND} .. -DBENCHMARK_ENABLE_TESTING=OFF -DBENCHMARK_INSTALL_DOCS=OFF -DBENCHMARK_ENABLE_GTEST_TESTS=OFF -DBENCHMARK_USE_BUNDLED_GTEST=OFF -DBENCHMARK_ENABLE_ASSEMBLY_TESTS=OFF && \
${MAKE_COMMAND} -j4 && \
${MAKE_COMMAND} install
# build jsoncpp
cd ${TOPDIR}/deps/3rd/jsoncpp && \
mkdir -p build && \
cd build && \
cmake -DJSONCPP_WITH_TESTS=OFF -DJSONCPP_WITH_POST_BUILD_UNITTEST=OFF .. && \
make && \
make install
${CMAKE_COMMAND} -DJSONCPP_WITH_TESTS=OFF -DJSONCPP_WITH_POST_BUILD_UNITTEST=OFF .. && \
${MAKE_COMMAND} && \
${MAKE_COMMAND} install
cd $current_dir
}
......
......@@ -32,8 +32,6 @@ See the Mulan PSL v2 for more details. */
namespace common {
const unsigned int ONE_KILO = 1024;
const unsigned int ONE_MILLION = ONE_KILO * ONE_KILO;
const unsigned int ONE_GIGA = ONE_MILLION * ONE_KILO;
const unsigned int FILENAME_LENGTH_MAX = 256; // the max filename length
const int LOG_STATUS_OK = 0;
......@@ -111,6 +109,11 @@ public:
int rotate(const int year = 0, const int month = 0, const int day = 0);
/**
* @brief 设置一个在日志中打印当前上下文信息的回调函数
* @details 比如设置一个获取当前session标识的函数,那么每次在打印日志时都会输出session信息。
* 这个回调函数返回了一个intptr_t类型的数据,可能返回字符串更好,但是现在够用了。
*/
void set_context_getter(std::function<intptr_t()> context_getter);
intptr_t context_id();
......@@ -328,13 +331,4 @@ int Log::out(const LOG_LEVEL console_level, const LOG_LEVEL log_level, T &msg)
*/
const char *lbt();
/**
* @brief 设置一个在日志中打印当前上下文信息的回调函数
* @details 比如设置一个获取当前session标识的函数,那么每次在打印日志时都会输出session信息。
* 这个回调函数返回了一个intptr_t类型的数据,可能返回字符串更好,但是现在够用了。
*/
void set_log_context_getter(std::function<intptr_t()> context_getter);
extern std::function<intptr_t()> g_context_getter;
} // namespace common
本篇文档介绍 MiniOB 中的事务模块是如何工作的。
# 背景
事务是数据库中非常基础的一个模块,也是非常核心的功能。事务有一些基本的概念,叫做ACID,分别是原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability)。如果你对事务的基本概念不太清楚,建议先学习了解事务的基本概念,比如学习[事务处理](lectures/lecture-6.md)章节,或者在网上搜索更多资料。
事务是数据库中非常基础的一个模块,也是非常核心的功能。事务有一些基本的概念,叫做ACID,分别是原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability)。如果你对事务的基本概念不太清楚,建议先学习了解事务的基本概念,比如学习[事务处理](../lectures/lecture-6.md)章节,或者在网上搜索更多资料。
# MiniOB 中的事务
## 实现简介
......
本篇文档介绍如何向[训练营](https://open.oceanbase.com/train)输出调试信息。
在使用训练营提交测试时,有时候会遇到本地环境没有问题,但是训练营上的输出总是不符合预期。但是训练营没有办法调试,这时候就需要在训练营上输出调试信息,以便于定位问题。
**如何输出调试信息**
可以参考文件 `sql_debug.h`,在需要输出调试信息的地方,调用`sql_debug`函数即可。sql_debug 的使用与打印日志类似,不过可以在向客户端输出正常结果后,再输出调试信息。
执行`sql_debug`同时会在日志中打印DEBUG级别的日志。
**示例**
1. `CreateTableStmt::create`
2. `TableScanPhysicalOperator::next`
**注意**
由于训练营上能够容纳的信息有限,所以输出太多信息会被截断。
**开关**
每个连接都可以开启或关闭调试,可以参考 `Session::sql_debug_`
在交互式命令行中,可以使用 `set sql_debug=1` 开启调试,使用 `set sql_debug=0` 关闭调试。
> 当前没有实现查看变量的命令。
示例
```bash
miniob > select * from t;
id
1
# get a tuple: 1
miniob > set sql_debug=0;
SUCCESS
miniob > select * from t;
id
1
```
\ No newline at end of file
......@@ -21,6 +21,9 @@
训练营的使用方法比较简单,不过这里也有一个小手册:
[训练营使用手册](https://ask.oceanbase.com/t/topic/35600372)
为了方便大家使用训练营时获取调试信息,这里有一个小手册:
[训练营调试输出手册](./debug-output.md)
注意,在训练营开始前,需要注意自己的程序输出需要满足一定的要求,请参考:
[提交测试需要满足的输出要求](./miniob-output-convention.md)
......
......@@ -20,7 +20,7 @@ See the Mulan PSL v2 for more details. */
#define MAX_CONNECTION_NUM "MAX_CONNECTION_NUM"
#define MAX_CONNECTION_NUM_DEFAULT 8192
#define PORT "PORT"
#define PORT_DEFAULT 16880
#define PORT_DEFAULT 6789
#define SOCKET_BUFFER_SIZE 8192
......
......@@ -71,6 +71,8 @@ See the Mulan PSL v2 for more details. */
DEFINE_RC(FILE_SEEK) \
DEFINE_RC(FILE_READ) \
DEFINE_RC(FILE_WRITE) \
DEFINE_RC(VARIABLE_NOT_EXISTS) \
DEFINE_RC(VARIABLE_NOT_VALID) \
DEFINE_RC(LOGBUF_FULL)
enum class RC
......
......@@ -19,10 +19,15 @@ See the Mulan PSL v2 for more details. */
#include "common/seda/stage_event.h"
#include "sql/executor/sql_result.h"
#include "event/sql_debug.h"
class Session;
class Communicator;
/**
* @brief 表示一个SQL请求
*
*/
class SessionEvent : public common::StageEvent
{
public:
......@@ -32,23 +37,17 @@ public:
Communicator *get_communicator() const;
Session *session() const;
void set_query(const std::string &query)
{
query_ = query;
}
void set_query(const std::string &query) { query_ = query; }
const std::string &query() const
{
return query_;
}
SqlResult *sql_result()
{
return &sql_result_;
}
const std::string &query() const { return query_; }
private:
Communicator *communicator_ = nullptr;
SqlResult sql_result_;
SqlResult *sql_result() { return &sql_result_; }
SqlDebug &sql_debug() { return sql_debug_; }
std::string query_;
private:
Communicator *communicator_ = nullptr; ///< 与客户端通讯的对象
SqlResult sql_result_; ///< SQL执行结果
SqlDebug sql_debug_; ///< SQL调试信息
std::string query_; ///< SQL语句
};
/* Copyright (c) 2021 OceanBase and/or its affiliates. All rights reserved.
miniob is licensed under Mulan PSL v2.
You can use this software according to the terms and conditions of the Mulan PSL v2.
You may obtain a copy of Mulan PSL v2 at:
http://license.coscl.org.cn/MulanPSL2
THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
See the Mulan PSL v2 for more details. */
//
// Created by Wangyunlai on 2023/6/29.
//
#include <stdarg.h>
#include "event/sql_debug.h"
#include "session/session.h"
#include "event/session_event.h"
using namespace std;
void SqlDebug::add_debug_info(const std::string &debug_info)
{
debug_infos_.push_back(debug_info);
}
void SqlDebug::clear_debug_info()
{
debug_infos_.clear();
}
const list<string> &SqlDebug::get_debug_infos() const
{
return debug_infos_;
}
void sql_debug(const char *fmt, ...)
{
Session *session = Session::current_session();
if (nullptr == session) {
return;
}
SessionEvent *request = session->current_request();
if (nullptr == request) {
return ;
}
SqlDebug &sql_debug = request->sql_debug();
const int buffer_size = 4096;
char *str = new char[buffer_size];
va_list ap;
va_start(ap, fmt);
vsnprintf(str, buffer_size, fmt, ap);
va_end(ap);
sql_debug.add_debug_info(str);
LOG_DEBUG("sql debug info: %s", str);
delete[] str;
}
/* Copyright (c) 2021 OceanBase and/or its affiliates. All rights reserved.
miniob is licensed under Mulan PSL v2.
You can use this software according to the terms and conditions of the Mulan PSL v2.
You may obtain a copy of Mulan PSL v2 at:
http://license.coscl.org.cn/MulanPSL2
THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
See the Mulan PSL v2 for more details. */
//
// Created by Wangyunlai on 2023/6/29.
//
#pragma once
#include <list>
#include <string>
/**
* @brief SQL调试信息
* @details
* 希望在运行SQL时,可以直接输出一些调试信息到客户端。
* 当前把调试信息都放在了session上,可以随着SQL语句输出。
* 但是现在还不支持与输出调试信息与行数据同步输出。
*/
class SqlDebug
{
public:
SqlDebug() = default;
virtual ~SqlDebug() = default;
void add_debug_info(const std::string &debug_info);
void clear_debug_info();
const std::list<std::string> &get_debug_infos() const;
private:
std::list<std::string> debug_infos_;
};
/**
* @brief 增加SQL的调试信息
* @details 可以在任何执行SQL语句时调用这个函数来增加调试信息。
* 如果当前上下文不在SQL执行过程中,那么不会生成调试信息。
* 在普通文本场景下,调试信息会直接输出到客户端,并增加 '#' 作为前缀。
*/
void sql_debug(const char *fmt, ...);
......@@ -23,6 +23,9 @@ class SessionEvent;
class Stmt;
class Command;
/**
* @brief 与SessionEvent类似,也是处理SQL请求的事件,只是用在SQL的不同阶段
*/
class SQLStageEvent : public common::StageEvent
{
public:
......@@ -74,8 +77,8 @@ public:
private:
SessionEvent *session_event_ = nullptr;
std::string sql_;
std::unique_ptr<Command> command_;
Stmt *stmt_ = nullptr;
std::unique_ptr<PhysicalOperator> operator_;
std::string sql_; ///< 处理的SQL语句
std::unique_ptr<Command> command_; ///< 语法解析后的SQL命令
Stmt *stmt_ = nullptr; ///< Resolver之后生成的数据结构
std::unique_ptr<PhysicalOperator> operator_; ///< 生成的执行计划,也可能没有
};
......@@ -973,9 +973,7 @@ RC MysqlCommunicator::send_result_rows(SqlResult *sql_result, bool no_column_def
break; // TODO send error packet
}
std::stringstream ss;
value.to_string(ss);
pos += store_lenenc_string(buf + pos, ss.str().c_str());
pos += store_lenenc_string(buf + pos, value.to_string().c_str());
}
int payload_length = pos - 4;
......
......@@ -23,6 +23,9 @@ See the Mulan PSL v2 for more details. */
PlainCommunicator::PlainCommunicator()
{
send_message_delimiter_.assign(1, '\0');
debug_message_prefix_.resize(2);
debug_message_prefix_[0] = '#';
debug_message_prefix_[1] = ' ';
}
RC PlainCommunicator::read_event(SessionEvent *&event)
......@@ -113,42 +116,67 @@ RC PlainCommunicator::write_state(SessionEvent *event, bool &need_disconnect)
need_disconnect = false;
delete[] buf;
writer_->flush();
return RC::SUCCESS;
}
RC PlainCommunicator::write_result(SessionEvent *event, bool &need_disconnect)
RC PlainCommunicator::write_debug(SessionEvent *request, bool &need_disconnect)
{
need_disconnect = true;
SqlResult *sql_result = event->sql_result();
if (nullptr == sql_result) {
const char *response = "Unexpected error: no result";
int len = strlen(response);
if (!session_->sql_debug_on()) {
return RC::SUCCESS;
}
RC rc = writer_->writen(response, len);
SqlDebug &sql_debug = request->sql_debug();
const std::list<std::string> &debug_infos = sql_debug.get_debug_infos();
for (auto &debug_info : debug_infos) {
RC rc = writer_->writen(debug_message_prefix_.data(), debug_message_prefix_.size());
if (OB_FAIL(rc)) {
LOG_ERROR("Failed to send data back to client. ret=%s, error=%s", strrc(rc), strerror(errno));
LOG_WARN("failed to send data to client. err=%s", strerror(errno));
need_disconnect = true;
return RC::IOERR_WRITE;
}
return rc;
rc = writer_->writen(debug_info.data(), debug_info.size());
if (OB_FAIL(rc)) {
LOG_WARN("failed to send data to client. err=%s", strerror(errno));
need_disconnect = true;
return RC::IOERR_WRITE;
}
rc = writer_->writen(send_message_delimiter_.data(), send_message_delimiter_.size());
char newline = '\n';
rc = writer_->writen(&newline, 1);
if (OB_FAIL(rc)) {
LOG_ERROR("Failed to send data back to client. ret=%s, error=%s", strrc(rc), strerror(errno));
return rc;
LOG_WARN("failed to send new line to client. err=%s", strerror(errno));
need_disconnect = true;
return RC::IOERR_WRITE;
}
}
need_disconnect = false;
return RC::SUCCESS;
}
RC PlainCommunicator::write_result(SessionEvent *event, bool &need_disconnect)
{
RC rc = write_result_internal(event, need_disconnect);
if (!need_disconnect) {
(void)write_debug(event, need_disconnect);
}
writer_->flush(); // TODO handle error
return rc;
}
RC PlainCommunicator::write_result_internal(SessionEvent *event, bool &need_disconnect)
{
RC rc = RC::SUCCESS;
need_disconnect = true;
SqlResult *sql_result = event->sql_result();
if (RC::SUCCESS != sql_result->return_code() || !sql_result->has_operator()) {
return write_state(event, need_disconnect);
}
RC rc = sql_result->open();
rc = sql_result->open();
if (OB_FAIL(rc)) {
sql_result->close();
sql_result->set_return_code(rc);
......@@ -215,9 +243,7 @@ RC PlainCommunicator::write_result(SessionEvent *event, bool &need_disconnect)
return rc;
}
std::stringstream ss;
value.to_string(ss);
std::string cell_str = ss.str();
std::string cell_str = value.to_string();
rc = writer_->writen(cell_str.data(), cell_str.size());
if (OB_FAIL(rc)) {
LOG_WARN("failed to send data to client. err=%s", strerror(errno));
......@@ -266,6 +292,5 @@ RC PlainCommunicator::write_result(SessionEvent *event, bool &need_disconnect)
rc = rc_close;
}
writer_->flush(); // TODO handle error
return rc;
}
\ No newline at end of file
......@@ -34,7 +34,10 @@ public:
private:
RC write_state(SessionEvent *event, bool &need_disconnect);
RC write_debug(SessionEvent *event, bool &need_disconnect);
RC write_result_internal(SessionEvent *event, bool &need_disconnect);
protected:
std::vector<char> send_message_delimiter_; ///< 发送消息分隔符
std::vector<char> debug_message_prefix_; ///< 调试信息前缀
};
......@@ -90,3 +90,13 @@ Session *Session::current_session()
{
return thread_session;
}
void Session::set_current_request(SessionEvent *request)
{
current_request_ = request;
}
SessionEvent *Session::current_request() const
{
return current_request_;
}
......@@ -18,11 +18,19 @@ See the Mulan PSL v2 for more details. */
class Trx;
class Db;
class SessionEvent;
/**
* @brief 表示会话
* @details 当前一个连接一个会话,没有做特殊的会话管理,这也简化了会话处理
*/
class Session
{
public:
// static Session &current();
/**
* @brief 获取默认的会话数据,新生成的会话都基于默认会话设置参数
* @note 当前并没有会话参数
*/
static Session &default_session();
public:
......@@ -35,18 +43,58 @@ public:
const char *get_current_db_name() const;
Db *get_current_db() const;
/**
* @brief 设置当前会话关联的数据库
*
* @param dbname 数据库名字
*/
void set_current_db(const std::string &dbname);
/**
* @brief 设置当前事务为多语句模式,需要明确的指出提交或回滚
*/
void set_trx_multi_operation_mode(bool multi_operation_mode);
/**
* @brief 当前事务是否为多语句模式
*/
bool is_trx_multi_operation_mode() const;
/**
* @brief 当前会话关联的事务
*
*/
Trx *current_trx();
/**
* @brief 设置当前正在处理的请求
*/
void set_current_request(SessionEvent *request);
/**
* @brief 获取当前正在处理的请求
*/
SessionEvent *current_request() const;
void set_sql_debug(bool sql_debug) { sql_debug_ = sql_debug; }
bool sql_debug_on() const { return sql_debug_; }
/**
* @brief 将指定会话设置到线程变量中
*
*/
static void set_current_session(Session *session);
/**
* @brief 获取当前的会话
* @details 当前某个请求开始时,会将会话设置到线程变量中,在整个请求处理过程中不会改变
*/
static Session *current_session();
private:
Db *db_ = nullptr;
Trx *trx_ = nullptr;
bool trx_multi_operation_mode_ = false; // 当前事务的模式,是否多语句模式. 单语句模式自动提交
SessionEvent *current_request_ = nullptr; ///< 当前正在处理的请求
bool trx_multi_operation_mode_ = false; ///< 当前事务的模式,是否多语句模式. 单语句模式自动提交
bool sql_debug_ = false; ///< 是否输出SQL调试信息
};
......@@ -116,6 +116,7 @@ void SessionStage::handle_request(StageEvent *event)
}
Session::set_current_session(sev->session());
sev->session()->set_current_request(sev);
SQLStageEvent *sql_event = new SQLStageEvent(sev, sql);
query_cache_stage_->handle_event(sql_event);
......@@ -126,5 +127,6 @@ void SessionStage::handle_request(StageEvent *event)
if (need_disconnect) {
Server::close_connection(communicator);
}
sev->session()->set_current_request(nullptr);
Session::set_current_session(nullptr);
}
......@@ -22,6 +22,7 @@ See the Mulan PSL v2 for more details. */
#include "sql/executor/show_tables_executor.h"
#include "sql/executor/trx_begin_executor.h"
#include "sql/executor/trx_end_executor.h"
#include "sql/executor/set_variable_executor.h"
#include "common/log/log.h"
RC CommandExecutor::execute(SQLStageEvent *sql_event)
......@@ -65,6 +66,11 @@ RC CommandExecutor::execute(SQLStageEvent *sql_event)
return executor.execute(sql_event);
}
case StmtType::SET_VARIABLE: {
SetVariableExecutor executor;
return executor.execute(sql_event);
}
case StmtType::EXIT: {
return RC::SUCCESS;
}
......
......@@ -29,6 +29,7 @@ using namespace std;
RC DescTableExecutor::execute(SQLStageEvent *sql_event)
{
RC rc = RC::SUCCESS;
Stmt *stmt = sql_event->stmt();
SessionEvent *session_event = sql_event->session_event();
Session *session = session_event->session();
......@@ -65,5 +66,5 @@ RC DescTableExecutor::execute(SQLStageEvent *sql_event)
sql_result->set_return_code(RC::SCHEMA_TABLE_NOT_EXIST);
sql_result->set_state_string("Table not exists");
}
return RC::SCHEMA_TABLE_NOT_EXIST;
return rc;
}
\ No newline at end of file
......@@ -26,7 +26,7 @@ class SelectStmt;
* @brief 执行SQL语句的Stage,包括DML和DDL
* @ingroup SQLStage
* @details 根据前面阶段生成的结果,有些语句会生成执行计划,有些不会。
* 整体上分为两类,带执行计划的,或者 @class CommandExecutor 可以直接执行的。
* 整体上分为两类,带执行计划的,或者 CommandExecutor 可以直接执行的。
*/
class ExecuteStage : public common::Stage
{
......
/* Copyright (c) 2021 OceanBase and/or its affiliates. All rights reserved.
miniob is licensed under Mulan PSL v2.
You can use this software according to the terms and conditions of the Mulan PSL v2.
You may obtain a copy of Mulan PSL v2 at:
http://license.coscl.org.cn/MulanPSL2
THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
See the Mulan PSL v2 for more details. */
//
// Created by Wangyunlai on 2023/6/14.
//
#pragma once
#include "common/rc.h"
#include "sql/operator/string_list_physical_operator.h"
#include "event/sql_event.h"
#include "event/session_event.h"
#include "sql/executor/sql_result.h"
#include "session/session.h"
#include "sql/stmt/set_variable_stmt.h"
/**
* @brief SetVariable语句执行器
* @ingroup Executor
*/
class SetVariableExecutor
{
public:
SetVariableExecutor() = default;
virtual ~SetVariableExecutor() = default;
RC execute(SQLStageEvent *sql_event)
{
RC rc = RC::SUCCESS;
Session *session = sql_event->session_event()->session();
SetVariableStmt *stmt = (SetVariableStmt *)sql_event->stmt();
const char *var_name = stmt->var_name();
const Value &var_value = stmt->var_value();
if (strcasecmp(var_name, "sql_debug") == 0) {
bool bool_value = false;
rc = var_value_to_boolean(var_value, bool_value);
if (rc != RC::SUCCESS) {
return rc;
}
session->set_sql_debug(bool_value);
LOG_TRACE("set sql_debug to %d", bool_value);
} else {
rc = RC::VARIABLE_NOT_EXISTS;
}
return RC::SUCCESS;
}
private:
RC var_value_to_boolean(const Value &var_value, bool &bool_value) const
{
RC rc = RC::SUCCESS;
if (var_value.attr_type() == AttrType::BOOLEANS) {
bool_value = var_value.get_boolean();
} else if (var_value.attr_type() == AttrType::INTS) {
bool_value = var_value.get_int() != 0;
} else if (var_value.attr_type() == AttrType::FLOATS) {
bool_value = var_value.get_float() != 0.0;
} else if (var_value.attr_type() == AttrType::CHARS) {
std::string true_strings[] = {
"true",
"on",
"yes",
"t",
"1"
};
std::string false_strings[] = {
"false",
"off",
"no",
"f",
"0"
};
for (size_t i = 0; i < sizeof(true_strings) / sizeof(true_strings[0]); i++) {
if (strcasecmp(var_value.get_string().c_str(), true_strings[i].c_str()) == 0) {
bool_value = true;
return rc;
}
}
for (size_t i = 0; i < sizeof(false_strings) / sizeof(false_strings[0]); i++) {
if (strcasecmp(var_value.get_string().c_str(), false_strings[i].c_str()) == 0) {
bool_value = false;
return rc;
}
}
rc = RC::VARIABLE_NOT_VALID;
}
return rc;
}
};
\ No newline at end of file
......@@ -24,7 +24,8 @@ class Session;
/**
* @brief SQL执行结果
* @details 如果当前SQL生成了执行计划,那么在返回客户端时,调用执行计划返回结果。
* @details
* 如果当前SQL生成了执行计划,那么在返回客户端时,调用执行计划返回结果。
* 否则返回的结果就是当前SQL的执行结果,比如DDL语句,通过return_code和state_string来描述。
* 如果出现了一些错误,也可以通过return_code和state_string来获取信息。
*/
......
......@@ -16,6 +16,7 @@ See the Mulan PSL v2 for more details. */
#include <memory>
#include <vector>
#include <string>
#include "common/log/log.h"
#include "sql/expr/tuple_cell.h"
......@@ -26,6 +27,27 @@ See the Mulan PSL v2 for more details. */
class Table;
/**
* @defgroup Tuple
* @brief Tuple 元组,表示一行数据,当前返回客户端时使用
* @details
* tuple是一种可以嵌套的数据结构。
* 比如select t1.a+t2.b from t1, t2;
* 需要使用下面的结构表示:
* @code {.cpp}
* Project(t1.a+t2.b)
* |
* Joined
* / \
* Row(t1) Row(t2)
* @endcode
*
*/
/**
* @brief 元组的结构,包含哪些字段(这里成为Cell),每个字段的说明
* @ingroup Tuple
*/
class TupleSchema
{
public:
......@@ -54,17 +76,63 @@ private:
std::vector<TupleCellSpec> cells_;
};
/**
* @brief 元组的抽象描述
* @ingroup Tuple
*/
class Tuple
{
public:
Tuple() = default;
virtual ~Tuple() = default;
/**
* @brief 获取元组中的Cell的个数
* @details 个数应该与tuple_schema一致
*/
virtual int cell_num() const = 0;
/**
* @brief 获取指定位置的Cell
*
* @param index 位置
* @param[out] cell 返回的Cell
*/
virtual RC cell_at(int index, Value &cell) const = 0;
/**
* @brief 根据cell的描述,获取cell的值
*
* @param spec cell的描述
* @param[out] cell 返回的cell
*/
virtual RC find_cell(const TupleCellSpec &spec, Value &cell) const = 0;
virtual std::string to_string() const
{
std::string str;
const int cell_num = this->cell_num();
for (int i = 0; i < cell_num - 1; i++) {
Value cell;
cell_at(i, cell);
str += cell.to_string();
str += ", ";
}
if (cell_num > 0) {
Value cell;
cell_at(cell_num - 1, cell);
str += cell.to_string();
}
return str;
}
};
/**
* @brief 一行数据的元组
* @ingroup Tuple
* @details 直接就是获取表中的一条记录
*/
class RowTuple : public Tuple
{
public:
......@@ -156,6 +224,13 @@ private:
std::vector<FieldExpr *> speces_;
};
/**
* @brief 从一行数据中,选择部分字段组成的元组,也就是投影操作
* @ingroup Tuple
* @details 一般在select语句中使用。
* 投影也可以是很复杂的操作,比如某些字段需要做类型转换、重命名、表达式运算、函数计算等。
* 当前的实现是比较简单的,只是选择部分字段,不做任何其他操作。
*/
class ProjectTuple : public Tuple
{
public:
......@@ -215,6 +290,10 @@ private:
Tuple *tuple_ = nullptr;
};
/**
* @brief 一些常量值组成的Tuple
* @ingroup Tuple
*/
class ValueListTuple : public Tuple
{
public:
......@@ -251,8 +330,9 @@ private:
};
/**
* 将两个tuple合并为一个tuple
* 在join算子中使用
* @brief 将两个tuple合并为一个tuple
* @ingroup Tuple
* @details 在join算子中使用
*/
class JoinedTuple : public Tuple
{
......@@ -274,28 +354,28 @@ public:
return left_->cell_num() + right_->cell_num();
}
RC cell_at(int index, Value &cell) const override
RC cell_at(int index, Value &value) const override
{
const int left_cell_num = left_->cell_num();
if (index > 0 && index < left_cell_num) {
return left_->cell_at(index, cell);
return left_->cell_at(index, value);
}
if (index >= left_cell_num && index < left_cell_num + right_->cell_num()) {
return right_->cell_at(index - left_cell_num, cell);
return right_->cell_at(index - left_cell_num, value);
}
return RC::NOTFOUND;
}
RC find_cell(const TupleCellSpec &spec, Value &cell) const override
RC find_cell(const TupleCellSpec &spec, Value &value) const override
{
RC rc = left_->find_cell(spec, cell);
RC rc = left_->find_cell(spec, value);
if (rc == RC::SUCCESS || rc != RC::NOTFOUND) {
return rc;
}
return right_->find_cell(spec, cell);
return right_->find_cell(spec, value);
}
private:
......
......@@ -14,6 +14,7 @@ See the Mulan PSL v2 for more details. */
#include "sql/operator/table_scan_physical_operator.h"
#include "storage/table/table.h"
#include "event/sql_debug.h"
using namespace std;
......@@ -48,8 +49,10 @@ RC TableScanPhysicalOperator::next()
}
if (filter_result) {
sql_debug("get a tuple: %s", tuple_.to_string().c_str());
break;
} else {
sql_debug("a tuple is filtered: %s", tuple_.to_string().c_str());
rc = RC::RECORD_EOF;
}
}
......
......@@ -19,6 +19,12 @@ See the Mulan PSL v2 for more details. */
class LogicalOperator;
/**
* @brief 简单比较的重写规则
* @ingroup Rewriter
* @details 如果有简单的比较运算,比如比较的两边都是常量,那我们就可以在运行执行计划之前就知道结果,
* 进而直接将表达式改成结果,这样就可以减少运行时的计算量。
*/
class ComparisonSimplificationRule : public ExpressionRewriteRule
{
public:
......
......@@ -19,8 +19,9 @@ See the Mulan PSL v2 for more details. */
class LogicalOperator;
/**
* 简化多个表达式联结的运算
* 比如只有一个表达式,或者表达式可以直接出来
* @brief 简化多个表达式联结的运算
* @ingroup Rewriter
* @details 比如只有一个表达式,或者表达式可以直接出来
*/
class ConjunctionSimplificationRule : public ExpressionRewriteRule
{
......
......@@ -28,7 +28,14 @@ class DeleteLogicalOperator;
class ExplainLogicalOperator;
class JoinLogicalOperator;
class PhysicalPlanGenerator {
/**
* @brief 物理计划生成器
* @ingroup PhysicalOperator
* @details 根据逻辑计划生成物理计划。
* 不会做任何优化,完全根据本意生成物理计划。
*/
class PhysicalPlanGenerator
{
public:
PhysicalPlanGenerator() = default;
virtual ~PhysicalPlanGenerator() = default;
......
......@@ -18,8 +18,9 @@ See the Mulan PSL v2 for more details. */
#include "sql/optimizer/rewrite_rule.h"
/**
* 将一些谓词表达式下推到表数据扫描中
* 这样可以提前过滤一些数据
* @brief 将一些谓词表达式下推到表数据扫描中
* @ingroup Rewriter
* @details 这样可以提前过滤一些数据
*/
class PredicatePushdownRewriter : public RewriteRule
{
......
......@@ -16,6 +16,11 @@ See the Mulan PSL v2 for more details. */
#include "sql/optimizer/rewrite_rule.h"
/**
* @brief 谓词重写规则
* @ingroup Rewriter
* @details 有些谓词可以在真正运行之前就知道结果,那么就可以提前运算出来,比如1=1,1=0。
*/
class PredicateRewriteRule : public RewriteRule
{
public:
......
......@@ -21,6 +21,10 @@ See the Mulan PSL v2 for more details. */
class LogicalOperator;
class Expression;
/**
* @brief 逻辑计划的重写规则
* @ingroup Rewriter
*/
class RewriteRule
{
public:
......@@ -29,6 +33,10 @@ public:
virtual RC rewrite(std::unique_ptr<LogicalOperator> &oper, bool &change_made) = 0;
};
/**
* @brief 表达式的重写规则
* @ingroup Rewriter
*/
class ExpressionRewriteRule
{
public:
......
......@@ -20,12 +20,30 @@ See the Mulan PSL v2 for more details. */
class LogicalOperator;
/**
* @defgroup Rewriter
* @brief 根据规则对逻辑计划进行重写
*/
/**
* @brief 根据一些规则对逻辑计划进行重写
* @ingroup Rewriter
* @details 当前仅实现了一两个非常简单的规则。
* 重写包括对逻辑计划和计划中包含的表达式。
*/
class Rewriter
{
public:
Rewriter();
virtual ~Rewriter() = default;
/**
* @brief 对逻辑计划进行重写
* @details 如果重写发生,change_made为true,否则为false。
* 通常情况下如果改写发生改变,就会继续重写,直到没有改变为止。
* @param oper 逻辑计划
* @param change_made 当前是否有重写发生
*/
RC rewrite(std::unique_ptr<LogicalOperator> &oper, bool &change_made);
private:
......
......@@ -187,6 +187,12 @@ struct LoadData
std::string file_name;
};
struct SetVariable
{
std::string name;
Value value;
};
class Command;
/**
......@@ -229,7 +235,7 @@ enum SqlCommandFlag
SCF_SYNC,
SCF_SHOW_TABLES,
SCF_DESC_TABLE,
SCF_BEGIN, /// 事务开始语句,可以在这里扩展只读事务
SCF_BEGIN, ///< 事务开始语句,可以在这里扩展只读事务
SCF_COMMIT,
SCF_CLOG_SYNC,
SCF_ROLLBACK,
......@@ -237,6 +243,7 @@ enum SqlCommandFlag
SCF_HELP,
SCF_EXIT,
SCF_EXPLAIN,
SCF_SET_VARIABLE, ///< 设置变量
};
/**
* @brief 表示一个SQL语句
......@@ -258,6 +265,7 @@ public:
DescTable desc_table;
LoadData load_data;
Explain explain;
SetVariable set_variable;
public:
Command();
......@@ -278,5 +286,5 @@ public:
}
private:
std::vector<std::unique_ptr<Command>> sql_commands_; /// 这里记录SQL命令。虽然看起来支持多个,但是当前仅处理一个
std::vector<std::unique_ptr<Command>> sql_commands_; ///< 这里记录SQL命令。虽然看起来支持多个,但是当前仅处理一个
};
......@@ -145,8 +145,9 @@ const char *Value::data() const
}
}
void Value::to_string(std::ostream &os) const
std::string Value::to_string() const
{
std::stringstream os;
switch (attr_type_) {
case INTS: {
os << num_value_.int_value_;
......@@ -164,6 +165,7 @@ void Value::to_string(std::ostream &os) const
LOG_WARN("unsupported attr type: %d", attr_type_);
} break;
}
return os.str();
}
int Value::compare(const Value &other) const
......@@ -258,9 +260,7 @@ float Value::get_float() const
std::string Value::get_string() const
{
std::stringstream ss;
to_string(ss);
return ss.str();
return this->to_string();
}
bool Value::get_boolean() const
......
......@@ -14,6 +14,8 @@ See the Mulan PSL v2 for more details. */
#pragma once
#include <string>
/**
* @brief 属性的类型
*
......@@ -67,7 +69,7 @@ public:
void set_string(const char *s, int len = 0);
void set_value(const Value &value);
void to_string(std::ostream &os) const;
std::string to_string() const;
int compare(const Value &other) const;
......
......@@ -132,6 +132,7 @@ int yyerror(YYLTYPE *llocp, ParsedSqlResult *sql_result, yyscan_t scanner, const
%type <command> rollback
%type <command> load_data
%type <command> explain
%type <command> set_variable
%type <command> help
%type <command> exit
%type <command> command_wrapper
......@@ -163,6 +164,7 @@ command_wrapper:
| rollback
| load_data
| explain
| set_variable
| help
| exit
;
......@@ -572,6 +574,17 @@ explain:
}
;
set_variable:
SET ID EQ value
{
$$ = new Command(SCF_SET_VARIABLE);
$$->set_variable.name = $2;
$$->set_variable.value = *$4;
free($2);
delete $4;
}
;
opt_semicolon: /*empty*/
| SEMICOLON
;
......
......@@ -13,9 +13,11 @@ See the Mulan PSL v2 for more details. */
//
#include "sql/stmt/create_table_stmt.h"
#include "event/sql_debug.h"
RC CreateTableStmt::create(Db *db, const CreateTable &create_table, Stmt *&stmt)
{
stmt = new CreateTableStmt(create_table.relation_name, create_table.attr_infos);
sql_debug("create table statement: table name %s", create_table.relation_name.c_str());
return RC::SUCCESS;
}
......@@ -9,35 +9,39 @@ MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
See the Mulan PSL v2 for more details. */
//
// Created by Wangyunlai on 2022/6/7.
// Created by Wangyunlai on 2023/6/29.
//
#pragma once
#include "common/seda/stage_event.h"
#include <string>
#include <vector>
class SQLStageEvent;
class Stmt;
#include "sql/stmt/stmt.h"
class OptimizeEvent : public common::StageEvent {
/**
* @brief SetVairable 语句,设置变量,当前是会话变量,但是只有会话变量,没有全局变量
* @ingroup Statement
*/
class SetVariableStmt : public Stmt
{
public:
OptimizeEvent(SQLStageEvent *sql_event, common::StageEvent *parent_event)
: sql_event_(sql_event), parent_event_(parent_event)
SetVariableStmt(const SetVariable &set_variable) : set_variable_(set_variable)
{}
virtual ~SetVariableStmt() = default;
virtual ~OptimizeEvent() noexcept = default;
StmtType type() const override { return StmtType::SET_VARIABLE; }
SQLStageEvent *sql_event() const
{
return sql_event_;
}
const char *var_name() const { return set_variable_.name.c_str(); }
const Value &var_value() const { return set_variable_.value; }
common::StageEvent *parent_event() const
static RC create(const SetVariable &set_variable, Stmt *&stmt)
{
return parent_event_;
/// 可以校验是否存在某个变量,但是这里忽略
stmt = new SetVariableStmt(set_variable);
return RC::SUCCESS;
}
private:
SQLStageEvent *sql_event_ = nullptr;
common::StageEvent *parent_event_ = nullptr;
SetVariable set_variable_;
};
\ No newline at end of file
......@@ -26,6 +26,7 @@ See the Mulan PSL v2 for more details. */
#include "sql/stmt/trx_begin_stmt.h"
#include "sql/stmt/trx_end_stmt.h"
#include "sql/stmt/exit_stmt.h"
#include "sql/stmt/set_variable_stmt.h"
RC Stmt::create_stmt(Db *db, const Command &cmd, Stmt *&stmt)
{
......@@ -79,6 +80,10 @@ RC Stmt::create_stmt(Db *db, const Command &cmd, Stmt *&stmt)
return ExitStmt::create(stmt);
}
case SCF_SET_VARIABLE: {
return SetVariableStmt::create(cmd.set_variable, stmt);
}
default: {
LOG_INFO("Command::type %d doesn't need to create statement.", cmd.flag);
} break;
......
......@@ -48,7 +48,8 @@ class Db;
DEFINE_ENUM_ITEM(HELP) \
DEFINE_ENUM_ITEM(EXIT) \
DEFINE_ENUM_ITEM(EXPLAIN) \
DEFINE_ENUM_ITEM(PREDICATE)
DEFINE_ENUM_ITEM(PREDICATE) \
DEFINE_ENUM_ITEM(SET_VARIABLE)
enum class StmtType {
#define DEFINE_ENUM_ITEM(name) name,
......
......@@ -17,6 +17,10 @@ See the Mulan PSL v2 for more details. */
#include "storage/table/table.h"
#include "storage/field/field_meta.h"
/**
* @brief 字段
*
*/
class Field
{
public:
......
......@@ -23,7 +23,10 @@ namespace Json {
class Value;
} // namespace Json
// Take care of shallow copy
/**
* @brief 字段元数据
*
*/
class FieldMeta
{
public:
......
......@@ -34,9 +34,6 @@ See the Mulan PSL v2 for more details. */
* @defgroup BPlusTree
*/
#define EMPTY_RID_PAGE_NUM -1 // TODO remove me
#define EMPTY_RID_SLOT_NUM -1
/**
* @brief B+树的操作类型
* @ingroup BPlusTree
......@@ -49,7 +46,7 @@ enum class BplusTreeOperationType
};
/**
* @brief 属性比较
* @brief 属性比较(BplusTree)
* @ingroup BPlusTree
*/
class AttrComparator
......@@ -91,7 +88,8 @@ private:
};
/**
* @brief 键值比较
* @brief 键值比较(BplusTree)
* @details BplusTree的键值除了字段属性,还有RID,是为了避免属性值重复而增加的。
* @ingroup BPlusTree
*/
class KeyComparator
......@@ -124,7 +122,7 @@ private:
};
/**
* @brief 属性打印,调试使用
* @brief 属性打印,调试使用(BplusTree)
* @ingroup BPlusTree
*/
class AttrPrinter
......@@ -173,7 +171,7 @@ private:
};
/**
* @brief 键值打印,调试使用
* @brief 键值打印,调试使用(BplusTree)
* @ingroup BPlusTree
*/
class KeyPrinter
......@@ -358,8 +356,7 @@ public:
/**
* 查找指定key的插入位置(注意不是key本身)
* 如果key已经存在,会设置found的值
* NOTE: 当前lookup的实现效率非常低,你是否可以优化它?
* 如果key已经存在,会设置found的值。
*/
int lookup(const KeyComparator &comparator, const char *key, bool *found = nullptr) const;
......@@ -418,11 +415,10 @@ public:
/**
* 与Leaf节点不同,lookup返回指定key应该属于哪个子节点,返回这个子节点在当前节点中的索引
* 如果想要返回插入位置,就提供 `insert_position` 参数
* @param comparator 用于键值比较的函数
* @param key 查找的键值
* @param found 如果是有效指针,将会返回当前是否存在指定的键值
* @param insert_position 如果是有效指针,将会返回可以插入指定键值的位置
* NOTE: 查找效率不高,你可以优化它吗?
* @param[in] comparator 用于键值比较的函数
* @param[in] key 查找的键值
* @param[out] found 如果是有效指针,将会返回当前是否存在指定的键值
* @param[out] insert_position 如果是有效指针,将会返回可以插入指定键值的位置
*/
int lookup(const KeyComparator &comparator,
const char *key,
......
......@@ -29,7 +29,10 @@ class IndexScanner;
class RecordDeleter;
class Trx;
// TODO remove the routines with condition
/**
* @brief 表
*
*/
class Table
{
public:
......
......@@ -22,6 +22,10 @@ See the Mulan PSL v2 for more details. */
#include "storage/index/index_meta.h"
#include "common/lang/serializable.h"
/**
* @brief 表元数据
*
*/
class TableMeta : public common::Serializable
{
public:
......
......@@ -55,8 +55,9 @@ private:
};
/**
* @brief 多版本并发事务
* @ingroup Transaction
* TODO 没有垃圾回收
*
*/
class MvccTrx : public Trx
{
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册