jjzjj

【StoneDB研发日志】union功能bug记录

关于StoneDB的星星点点 2023-03-28 原文

1、问题现象

create database syw_mtr;
use syw_mtr;
CREATE TABLE t1 (f1 VARCHAR(255) CHARACTER SET utf8)engine=tianmu;
CREATE TABLE t2 AS SELECT LEFT(f1,171) AS f2 FROM t1 UNION SELECT LEFT(f1,171) AS f2 FROM t1;
ERROR 2013 (HY000): Lost connection to MySQL server during query

问题issue:https://github.com/stoneatom/stonedb/issues/226

2、问题原因

bug代码行

Query_result_create::prepare (sql_insert.cc:2753)

create_table->table不为空

assert(create_table->table == NULL);

stonedb堆栈

    libc.so.6!raise (未知源:0)
    libc.so.6!abort (未知源:0)
    libc.so.6!__assert_fail_base (未知源:0)
    libc.so.6!__assert_fail (未知源:0)
    Query_result_create::prepare(Query_result_create * const this, List<Item> & values, SELECT_LEX_UNIT * u) (\opt\litaihong\stonedb\sql\sql_insert.cc:2753)
    st_select_lex::prepare(st_select_lex * const this, THD * thd) (\opt\litaihong\stonedb\sql\sql_resolver.cc:361)
    st_select_lex_unit::prepare_fake_select_lex(st_select_lex_unit * const this, THD * thd_arg) (\opt\litaihong\stonedb\sql\sql_union.cc:441)
    st_select_lex_unit::optimize_for_stonedb(st_select_lex_unit * const this) (\opt\litaihong\stonedb\storage\stonedb\core\engine_execute.cpp:586)
    stonedb::core::Engine::HandleSelect(stonedb::core::Engine * const this, THD * thd, LEX * lex, Query_result *& result, ulong setup_tables_done_option, int & res, int & optimize_after_sdb, int & sdb_free_join, int with_insert) (\opt\litaihong\stonedb\storage\stonedb\core\engine_execute.cpp:181)
    stonedb::dbhandler::SDB_HandleSelect(THD * thd, LEX * lex, Query_result *& result, ulong setup_tables_done_option, int & res, int & optimize_after_sdb, int & sdb_free_join, int with_insert) (\opt\litaihong\stonedb\storage\stonedb\handler\ha_rcengine.cpp:82)
    mysql_execute_command(THD * thd, bool first_level) (\opt\litaihong\stonedb\sql\sql_parse.cc:3265)
    mysql_parse(THD * thd, Parser_state * parser_state) (\opt\litaihong\stonedb\sql\sql_parse.cc:5621)
    dispatch_command(THD * thd, const COM_DATA * com_data, enum_server_command command) (\opt\litaihong\stonedb\sql\sql_parse.cc:1495)
    do_command(THD * thd) (\opt\litaihong\stonedb\sql\sql_parse.cc:1034)
    handle_connection(void * arg) (\opt\litaihong\stonedb\sql\conn_handler\connection_handler_per_thread.cc:313)
    pfs_spawn_thread(void * arg) (\opt\litaihong\stonedb\storage\perfschema\pfs.cc:2197)
    libpthread.so.0!start_thread (未知源:0)
    libc.so.6!clone (未知源:0)

分析过程

assert(create_table->table == NULL);

Query_result_create::prepare (sql_insert.cc:2753)函数prepare阶段和optimize阶段在被调用了两次

1、stonedb::core::Engine::HandleSelect函数首先调用

st_select_lex_unit::prepare->prepare_fake_select_lex ->Query_result_create::prepare

此时create_table->table 是NULL。

2、然后调用

st_select_lex_unit::optimize_for_stonedb -> prepare_fake_select_lex -> Query_result_create::prepare

此时create_table->table不为NULL,assert(create_table->table == NULL); 导致数据库异常退出。

问题出现原因

分析st_select_lex_unit::optimize_for_stonedb函数发现是来源于MySQL的st_select_lex_unit::exec()函数

3、解决办法

mysql的handle_query函数先有prepare然后是optimize函数,但我们stonedb的optimize函数是根据MySQL的st_select_lex_unit::exec()写的,不知道为啥没有正常的optimize流程

两种解决方案:

1、修改抛异常的代码行Query_result_create::prepare (sql_insert.cc:2753)函数

assert(create_table->table == NULL);

2、重写stonedb的st_select_lex_unit::optimize_for_stonedb函数,改动比较大,涉及功能多。

解决方案更新:

A:我们发现,这个问题是由于在优化器的代码中,Lex unit 两次prepare造成的。

首先我们抛弃了对assert语句修改的考虑,因为那里判断表没有创建是合理的。

于是解决方案有两种,

1:考虑在lex unit 的prepare里加入 is_prepared的判断。但这个方案,影响面很大,因为这个函数所有的语法都会影响到。

2:针对,select union 或者 select join,以及他们出现的复杂的组合做修改。发现了三处异常点需要进行is_prepared判断。1:union处。2:join处。3:fake_lex_prepare处。

B:针对我们的修改添加了相应mtr语句进行覆盖。一并记录在issue226中。

https://github.com/stoneatom/stonedb/issues/226

ADD test cases to cover these ctas queries for the following reason.

1: From code perspective. There are enough evidences that we want to support these ctas.

2: Our fix can cover these related queries.

CREATE TABLE t2 AS SELECT LEFT(f1,171) AS f2 FROM t1 UNION SELECT LEFT(f1,171) AS f2 FROM t1;

CREATE TABLE t3 AS SELECT t1.f1 AS f3 FROM t1 LEFT JOIN t2 ON t1.f1 = t2.f2;

CREATE TABLE t4 AS SELECT t1.f1 AS f4 FROM t1 INNER JOIN t2 ON t1.f1 = t2.f2;

CREATE TABLE t5 AS SELECT t1.f1 AS f5 FROM t1 RIGHT JOIN t2 ON t1.f1 = t2.f2;

CREATE TABLE t6 AS SELECT t1.f1 AS f6 FROM t1 UNION SELECT t2.f2 AS f6 FROM t2 LEFT JOIN t3 ON t2.f2 = t3.f3;

CREATE TABLE t7 AS SELECT t1.f1 AS f7 FROM t1 UNION SELECT t2.f2 AS f7 FROM t2 INNER JOIN t3 ON t2.f2 = t3.f3;

CREATE TABLE t8 AS SELECT t1.f1 AS f8 FROM t1 UNION SELECT t2.f2 AS f8 FROM t2 RIGHT JOIN t3 ON t2.f2 = t3.f3;

CREATE TABLE t9 AS SELECT t1.f1 AS f9 FROM t1 INNER JOIN t3 ON t1.f1 = t3.f3 UNION SELECT t2.f2 AS f9 FROM t2;

4、MySQL正常流程堆栈

Query_result_create::prepare(Query_result_create * const this, List<Item> & values, SELECT_LEX_UNIT * u) (\opt\litaihong\stonedb\sql\sql_insert.cc:2750)
st_select_lex::prepare(st_select_lex * const this, THD * thd) (\opt\litaihong\stonedb\sql\sql_resolver.cc:361)
st_select_lex_unit::prepare_fake_select_lex(st_select_lex_unit * const this, THD * thd_arg) (\opt\litaihong\stonedb\sql\sql_union.cc:441)
st_select_lex_unit::prepare(st_select_lex_unit * const this, THD * thd_arg, Query_result * sel_result, ulonglong added_options, ulonglong removed_options) (\opt\litaihong\stonedb\sql\sql_union.cc:670)
handle_query(THD * thd, LEX * lex, Query_result * result, ulonglong added_options, ulonglong removed_options, int optimize_after_bh, int free_join_from_bh) (\opt\litaihong\stonedb\sql\sql_select.cc:150)
mysql_execute_command(THD * thd, bool first_level) (\opt\litaihong\stonedb\sql\sql_parse.cc:3266)
mysql_parse(THD * thd, Parser_state * parser_state) (\opt\litaihong\stonedb\sql\sql_parse.cc:5621)
dispatch_command(THD * thd, const COM_DATA * com_data, enum_server_command command) (\opt\litaihong\stonedb\sql\sql_parse.cc:1495)
do_command(THD * thd) (\opt\litaihong\stonedb\sql\sql_parse.cc:1034)
handle_connection(void * arg) (\opt\litaihong\stonedb\sql\conn_handler\connection_handler_per_thread.cc:313)
pfs_spawn_thread(void * arg) (\opt\litaihong\stonedb\storage\perfschema\pfs.cc:2197)
libpthread.so.0!start_thread (未知源:0)
libc.so.6!clone (未知源:0)

有关【StoneDB研发日志】union功能bug记录的更多相关文章

  1. ruby - Sinatra:运行 rspec 测试时记录噪音 - 2

    Sinatra新手;我正在运行一些rspec测试,但在日志中收到了一堆不需要的噪音。如何消除日志中过多的噪音?我仔细检查了环境是否设置为:test,这意味着记录器级别应设置为WARN而不是DEBUG。spec_helper:require"./app"require"sinatra"require"rspec"require"rack/test"require"database_cleaner"require"factory_girl"set:environment,:testFactoryGirl.definition_file_paths=%w{./factories./test/

  2. ruby-on-rails - Rails 5 Active Record 记录无效错误 - 2

    我有两个Rails模型,即Invoice和Invoice_details。一个Invoice_details属于Invoice,一个Invoice有多个Invoice_details。我无法使用accepts_nested_attributes_forinInvoice通过Invoice模型保存Invoice_details。我收到以下错误:(0.2ms)BEGIN(0.2ms)ROLLBACKCompleted422UnprocessableEntityin25ms(ActiveRecord:4.0ms)ActiveRecord::RecordInvalid(Validationfa

  3. ruby-on-rails - Cucumber 是否只是 rspec 的包装器以帮助将测试组织成功能? - 2

    只是想确保我理解了事情。据我目前收集到的信息,Cucumber只是一个“包装器”,或者是一种通过将事物分类为功能和步骤来组织测试的好方法,其中实际的单元测试处于步骤阶段。它允许您根据事物的工作方式组织您的测试。对吗? 最佳答案 有点。它是一种组织测试的方式,但不仅如此。它的行为就像最初的Rails集成测试一样,但更易于使用。这里最大的好处是您的session在整个Scenario中保持透明。关于Cucumber的另一件事是您(应该)从使用您的代码的浏览器或客户端的角度进行测试。如果您愿意,您可以使用步骤来构建对象和设置状态,但通常您

  4. ruby-on-rails - 事件记录 : Select max of limit - 2

    我正在尝试将以下SQL查询转换为ActiveRecord,它正在融化我的大脑。deletefromtablewhereid有什么想法吗?我想做的是限制表中的行数。所以,我想删除少于最近10个条目的所有内容。编辑:通过结合以下几个答案找到了解决方案。Temperature.where('id这给我留下了最新的10个条目。 最佳答案 从您的SQL来看,您似乎想要从表中删除前10条记录。我相信到目前为止的大多数答案都会如此。这里有两个额外的选择:基于MurifoX的版本:Table.where(:id=>Table.order(:id).

  5. Ruby 守护进程导致 ActiveRecord 记录器 IOError - 2

    我目前正在用Ruby编写一个项目,它使用ActiveRecordgem进行数据库交互,我正在尝试使用ActiveRecord::Base.logger记录所有数据库事件具有以下代码的属性ActiveRecord::Base.logger=Logger.new(File.open('logs/database.log','a'))这适用于迁移等(出于某种原因似乎需要启用日志记录,因为它在禁用时会出现NilClass错误)但是当我尝试运行包含调用ActiveRecord对象的线程守护程序的项目时脚本失败并出现以下错误/System/Library/Frameworks/Ruby.frame

  6. ruby-on-rails - 在 Rails 中更高效地查找或创建多条记录 - 2

    我有一个应用需要发送用户事件邀请。当用户邀请friend(用户)参加事件时,如果尚不存在将用户连接到该事件的新记录,则会创建该记录。我的模型由用户、事件和events_user组成。classEventdefinvite(user_id,*args)user_id.eachdo|u|e=EventsUser.find_or_create_by_event_id_and_user_id(self.id,u)e.save!endendend用法Event.first.invite([1,2,3])我不认为以上是完成我的任务的最有效方法。我设想了一种方法,例如Model.find_or_cr

  7. ruby - 在模块/类之间共享全局记录器 - 2

    在许多ruby​​类之间共享记录器实例的最佳(正确)方法是什么?现在我只是将记录器创建为全局$logger=Logger.new变量,但我觉得有更好的方法可以在不使用全局变量的情况下执行此操作。如果我有以下内容:moduleFooclassAclassBclassC...classZend在所有类之间共享记录器实例的最佳方式是什么?我是以某种方式在Foo模块中声明/创建记录器还是只是使用全局$logger没问题? 最佳答案 在模块中添加常量:moduleFooLogger=Logger.newclassAclassBclassC..

  8. ruby-on-rails - rails 功能测试 - 2

    在Rails自动生成的功能测试(test/functional/products_controller_test.rb)中,我看到以下代码:classProductsControllerTest我的问题是:方法调用products()在哪里/如何定义?products(:one)到底是什么意思?看代码,大概意思是“创建一个产品”,但是它是如何工作的呢?注意我是Ruby/Rails的新手,如果这些是微不足道的问题,我深表歉意。 最佳答案 如果您查看test/fixtures文件夹,您会看到一个products.yml文件。这是在您创建

  9. ruby - Sinatra 中的全局救援和日志记录异常 - 2

    如何在出现异常时指定全局救援,如果您将Sinatra用于API或应用程序,您将如何处理日志记录? 最佳答案 404可以在not_found方法的帮助下处理,例如:not_founddo'Sitedoesnotexist.'end500s可以通过调用带有block的错误方法来处理,例如:errordo"Applicationerror.Plstrylater."end错误的详细信息可以通过request.env中的sinatra.error访问,如下所示:errordo'Anerroroccured:'+request.env['si

  10. ruby-on-rails - 使用 Ruby 标准 Logger 每天只创建一个日志 - 2

    我正在使用ruby​​标准记录器,我想要每天轮换一次,所以在我的代码中我有:Logger.new("#{$ROOT_PATH}/log/errors.log",'daily')它运行完美,但它创建了两个文件errors.log.20130217和errors.log.20130217.1。如何强制它每天只创建一个文件? 最佳答案 您的代码对于长时间运行的应用程序是正确的。发生的事情是您在给定的一天多次运行代码。第一次运行时,Ruby会创建一个日志文件“errors.log”。当日期改变时,Ruby将文件重命名为“errors.log

随机推荐