2023-06-05 stonedb-在聚合的场景查询为空无法执行case属性-问题分析-及定位问题的思路

news2024/11/16 5:47:22

目录

摘要:

查询SQL:

处理该问题的思路分析:

削减查询SQL的操作符

tianmu引擎的查询SQL

innodb引擎的查询SQL

mysql/sql和innodb执行分析:

执行过程的trace日志:

摘出一些涉及查询执行的部分

思路分析:

mysql/sql层的核心处理:

Item_func_case::fix_fields

调用堆栈:

函数实现:

参数ref的数据:

函数目的分析:

Item_func_case::val_int

调用堆栈:

函数实现:

函数实现的运算逻辑的数据:

函数目的分析:

tianmu执行分析:

构建查询序列:

tianmu的构建查询序列的分析思路:

tianmu执行的核心处理:

MysqlExpression::EvalType

调用堆栈:

函数实现:

函数目的分析:

MysqlExpression::GetTianmufieldItem

调用堆栈:

函数实现:

函数目的分析:

Tianmu::core::Query::Preexecute

执行结果的临时表的case里属性的数据:

此函数目的:

Query::AddColumnForMysqlExpression

调用堆栈:

函数实现:

此函数目的:

Tianmu::core::Query::AddColumnForMysqlExpression

调用堆栈:

函数实现:

此函数目的:

TempTable::Materialize

调用堆栈:

函数实现:

此函数执行完后的case列属性中的数据

此函数目的:

AggregationAlgorithm::MultiDimensionalGroupByScan

调用堆栈:

对比当派生中有值且聚合能非NULL的结果时的数据:

此函数目的:

分析该查询出错的问题总结:

tianmu引擎对于case列属性以及聚合处理的设计意图:

修复该查询错误的问题:


摘要:

stonedb-在派生表的场景查询为空无法传递默认值-问题分析.

本文对该问题的成因, 相关功能的代码设计, 在下一步设计时如何应对这种问题, 做相关的分析。

https://stoneatom.yuque.com/staff-ft8n1u/lsztbl/rxlhws22n0f1otxn/edit#AqyB

相关ISSUE: https://github.com/stoneatom/stonedb/issues/1784

查询SQL:

SELECT 'aaaaa' inner_code,
       a.exchange_rate,
       sum(a.balance) AS balance,
       'b' std_balance
  FROM (SELECT b.inner_code,
               CASE
                 WHEN r.row_id IS NOT NULL THEN
                  r.exchange_rate
                 ELSE
                  1.0000
               END AS exchange_rate,
               c.fiscal_date,
               c.balance
          FROM c1md_bank_acct a
          JOIN c1md_company b
            ON a.company_id = b.row_id
           AND b.deleted_flag = '0'
          LEFT JOIN c1cd_exchange_rate r
            ON a.currency_id = r.currency_id
           AND r.deleted_flag = '0'
           AND r.using_flag = '1'
           AND r.inure_date <= now()
           AND (r.abate_date >= now() OR r.abate_date IS NULL)
          LEFT JOIN (SELECT b.account_id, b.fiscal_date, b.balance
                      FROM (SELECT account_id, max(fiscal_date) fiscal_date
                              FROM c1am_acct_day
                             WHERE deleted_flag = '0'
                             GROUP BY account_id) a
                      JOIN c1am_acct_day b
                        ON a.account_id = b.account_id
                       AND a.fiscal_date = b.fiscal_date
                     WHERE b.deleted_flag = '0') c
            ON a.row_id = c.account_id
         WHERE a.deleted_flag = '0'
           AND a.acct_flag IN ('2', '4')
           AND a.company_id IN (3000000000027265)) a;

处理该问题的思路分析:

  1. 对于复杂的查询SQL, 涉及的操作符过多, 首先要进行问题定位
    1. 缩减到具体哪些操作符的执行出现了问题
    2. 这个过程涉及条件削减, 排查, 甚至摸索试探
  2. 找到出问题的操作符的对应的代码实现, 分析该处代码的设计意图,以及为什么会导致当前查询出错
    1. 该过程涉及架构能力,代码能力,以及理解他人代码设计意图的能力
    2. 对于复杂的代码, 牵扯到的因素过多,存在试探来猜测设计意图的情况, 但是一定需要事实的验证来辅助对设计意图的定位
  3. 重新对该操作符做设计
    1. 需要注意要兼容此前的设计意图
    2. 为了避免出现类似的问题, 需要评估代码修改对其他模块的影响

削减查询SQL的操作符

tianmu引擎的查询SQL

create table t1 (age int) engine=tianmu;

select case when  age IS NOT NULL THEN age else 33 end,  sum(age) from (  select * from t1) ta;

innodb引擎的查询SQL

create table t1_innodb (age int) engine=innodb;

select case when  age IS NOT NULL THEN age else 33 end,  sum(age) from (  select * from t1_innodb) ta;

mysql/sql和innodb执行分析:

执行过程的trace日志:

2022-10-18 mysql-5.7-开启debug日志_mysql 5.7 debug_财阀悟世的博客-CSDN博客

https://download.csdn.net/download/adofsauron/87864107

摘出一些涉及查询执行的部分

T@3:  2008:          sql_executor.cc:        887: | | | | | | >do_select
T@3:  2009:          sql_executor.cc:       1226: | | | | | | | >sub_select
T@3:  2010:               records.cc:        194: | | | | | | | | >init_read_record
T@3:  2011:               records.cc:        333: | | | | | | | | | info: using rr_sequential
T@3:  2012:               handler.cc:       2924: | | | | | | | | | >ha_rnd_init
T@3:  2013:             ha_innodb.cc:       8960: | | | | | | | | | | >change_active_index
T@3:  2014:             ha_innodb.cc:       8905: | | | | | | | | | | | >innobase_get_index
T@3:  2015:             ha_innodb.cc:       8946: | | | | | | | | | | | <innobase_get_index 8946
T@3:  2016:             ha_innodb.cc:       9064: | | | | | | | | | | <change_active_index 9064
T@3:  2017:               handler.cc:       2931: | | | | | | | | | <ha_rnd_init 2931
T@3:  2018:             ha_innodb.cc:       2887: | | | | | | | | | >ha_innobase::update_thd
T@3:  2019:             ha_innodb.cc:       2889: | | | | | | | | | | ha_innobase::update_thd: user_thd: 0x7fd254000e10 -> 0x7fd254000e10
T@3:  2020:             ha_innodb.cc:       2618: | | | | | | | | | | >innobase_trx_init
T@3:  2021:             ha_innodb.cc:       2628: | | | | | | | | | | <innobase_trx_init 2628
T@3:  2022:             ha_innodb.cc:       2913: | | | | | | | | | <ha_innobase::update_thd 2913
T@3:  2023:               records.cc:        362: | | | | | | | | <init_read_record 362
T@3:  2024:               handler.cc:       2971: | | | | | | | | >handler::ha_rnd_next
T@3:  2025:             ha_innodb.cc:       9315: | | | | | | | | | >rnd_next
T@3:  2026:             ha_innodb.cc:       9218: | | | | | | | | | | >index_first
T@3:  2027:             ha_innodb.cc:       8711: | | | | | | | | | | | >index_read
T@3:  2028:               row0sel.cc:       4681: | | | | | | | | | | | | >row_search_mvcc
T@3:  2029:               row0sel.cc:       6434: | | | | | | | | | | | | <row_search_mvcc 6434
T@3:  2030:             ha_innodb.cc:       8871: | | | | | | | | | | | <index_read 8871
T@3:  2031:             ha_innodb.cc:       9230: | | | | | | | | | | <index_first 9230
T@3:  2032:             ha_innodb.cc:       9331: | | | | | | | | | <rnd_next 9331
T@3:  2033:               handler.cc:       2986: | | | | | | | | <handler::ha_rnd_next 2986
T@3:  2034:          sql_executor.cc:       1316: | | | | | | | <sub_select 1316
T@3:  2035:          sql_executor.cc:       1226: | | | | | | | >sub_select
T@3:  2036:          sql_executor.cc:       3019: | | | | | | | | >end_send_group
T@3:  2037:              item_sum.cc:       1438: | | | | | | | | | >Item_sum_sum::clear
T@3:  2038:              item_sum.cc:       1447: | | | | | | | | | <Item_sum_sum::clear 1447
T@3:  2039:             sql_class.cc:       2726: | | | | | | | | | >Query_result_send::send_data
T@3:  2040:             sql_class.cc:       4753: | | | | | | | | | | >send_result_set_row
T@3:  2041:             sql_class.cc:       4765: | | | | | | | | | | <send_result_set_row 4765
T@3:  2042:             sql_class.cc:       2749: | | | | | | | | | <Query_result_send::send_data 2749
T@3:  2043:      protocol_classic.cc:       1202: | | | | | | | | | >Protocol_classic::end_row
T@3:  2044:      protocol_classic.cc:       1205: | | | | | | | | | <Protocol_classic::end_row 1205
T@3:  2045:          sql_executor.cc:        355: | | | | | | | | | packet_header: Memory: 0x7fd519f6e944  Bytes: (4)
04 00 00 04 
T@3:  2046:          sql_executor.cc:       3084: | | | | | | | | <end_send_group 3084
T@3:  2047:          sql_executor.cc:       1234: | | | | | | | <sub_select 1234
T@3:  2048:            sql_select.cc:       2546: | | | | | | | >JOIN::join_free
T@3:  2049:            sql_select.cc:       2608: | | | | | | | | >JOIN::cleanup
T@3:  2050:               handler.cc:       2945: | | | | | | | | | >ha_rnd_end
T@3:  2051:               handler.cc:       2953: | | | | | | | | | <ha_rnd_end 2953
T@3:  2052:             ha_innodb.cc:       8580: | | | | | | | | | >index_end
T@3:  2053:               handler.cc:       6761: | | | | | | | | | | >DsMrr_impl::dsmrr_close
T@3:  2054:               handler.cc:       6770: | | | | | | | | | | <DsMrr_impl::dsmrr_close 6770
T@3:  2055:             ha_innodb.cc:       8592: | | | | | | | | | <index_end 8592
T@3:  2056:              sql_base.cc:       1147: | | | | | | | | | >free_io_cache
T@3:  2057:              sql_base.cc:       1154: | | | | | | | | | <free_io_cache 1154
T@3:  2058:              filesort.cc:        591: | | | | | | | | | >filesort_free_buffers
T@3:  2059:              filesort.cc:        604: | | | | | | | | | <filesort_free_buffers 604
T@3:  2060:            sql_select.cc:       2656: | | | | | | | | <JOIN::cleanup 2656
T@3:  2061:                  lock.cc:        449: | | | | | | | | >mysql_unlock_read_tables
T@3:  2062:                  lock.cc:        666: | | | | | | | | | >unlock_external
T@3:  2063:               handler.cc:       8073: | | | | | | | | | | >handler::ha_external_lock
T@3:  2064:             ha_innodb.cc:      15636: | | | | | | | | | | | >ha_innobase::external_lock
T@3:  2065:             ha_innodb.cc:      15637: | | | | | | | | | | | | enter: lock_type: 2
T@3:  2066:             ha_innodb.cc:       2887: | | | | | | | | | | | | >ha_innobase::update_thd
T@3:  2067:             ha_innodb.cc:       2889: | | | | | | | | | | | | | ha_innobase::update_thd: user_thd: 0x7fd254000e10 -> 0x7fd254000e10
T@3:  2068:             ha_innodb.cc:       2618: | | | | | | | | | | | | | >innobase_trx_init
T@3:  2069:             ha_innodb.cc:       2628: | | | | | | | | | | | | | <innobase_trx_init 2628
T@3:  2070:             ha_innodb.cc:       2913: | | | | | | | | | | | | <ha_innobase::update_thd 2913
T@3:  2071:             ha_innodb.cc:       4379: | | | | | | | | | | | | >innobase_commit
T@3:  2072:             ha_innodb.cc:       4381: | | | | | | | | | | | | | trans: ending transaction
T@3:  2073:             ha_innodb.cc:       2618: | | | | | | | | | | | | | >innobase_trx_init
T@3:  2074:             ha_innodb.cc:       2628: | | | | | | | | | | | | | <innobase_trx_init 2628
T@3:  2075:             ha_innodb.cc:       4515: | | | | | | | | | | | | <innobase_commit 4515
T@3:  2076:             ha_innodb.cc:      15895: | | | | | | | | | | | <ha_innobase::external_lock 15895
T@3:  2077:               handler.cc:       8135: | | | | | | | | | | <handler::ha_external_lock 8135
T@3:  2078:                  lock.cc:        682: | | | | | | | | | <unlock_external 682
T@3:  2079:                  lock.cc:        499: | | | | | | | | <mysql_unlock_read_tables 499
T@3:  2080:            sql_select.cc:       2590: | | | | | | | <JOIN::join_free 2590
T@3:  2081:             sql_error.cc:        409: | | | | | | | >set_eof_status
T@3:  2082:             sql_error.cc:        431: | | | | | | | <set_eof_status 431
T@3:  2083:          sql_executor.cc:       1008: | | | | | | | info: 1 records output
T@3:  2084:          sql_executor.cc:       1019: | | | | | | <do_select 1019
T@3:  2085:          sql_executor.cc:        210: | | | | | | counts: thd->examined_row_count: 0
T@3:  2086:          sql_executor.cc:        212: | | | | | <JOIN::exec 212
T@3:  2087:            sql_select.cc:        286: | | | | | opt: steps: ending struct
T@3:  2088:            sql_select.cc:        286: | | | | | opt: join_execution: ending struct
T@3:  2089:            sql_select.cc:        286: | | | | | opt: (null): ending struct
T@3:  2090:            sql_select.cc:        741: | | | | | THD::enter_stage: 'end' /root/work/stonedb-dev-20230605/sql/sql_select.cc:210
T@3:  2091:           sql_profile.cc:        365: | | | | | >PROFILING::status_change
T@3:  2092:           sql_profile.cc:        371: | | | | | <PROFILING::status_change 371
T@3:  2093:             sql_union.cc:        920: | | | | | >st_select_lex_unit::cleanup
T@3:  2094:             sql_union.cc:       1078: | | | | | | >st_select_lex::cleanup()
T@3:  2095:            sql_select.cc:       2608: | | | | | | | >JOIN::cleanup
T@3:  2096:              sql_base.cc:       1147: | | | | | | | | >free_io_cache
T@3:  2097:              sql_base.cc:       1154: | | | | | | | | <free_io_cache 1154
T@3:  2098:              filesort.cc:        591: | | | | | | | | >filesort_free_buffers
T@3:  2099:              filesort.cc:        604: | | | | | | | | <filesort_free_buffers 604
T@3:  2100:            sql_select.cc:       2656: | | | | | | | <JOIN::cleanup 2656
T@3:  2101:             sql_union.cc:       1100: | | | | | | <st_select_lex::cleanup() 1100
T@3:  2102:             sql_union.cc:        952: | | | | | <st_select_lex_unit::cleanup 952
T@3:  2103:            sql_select.cc:        215: | | | | <handle_query 215
T@3:  2104:             sql_parse.cc:        741: | | | | THD::enter_stage: 'query end' /root/work/stonedb-dev-20230605/sql/sql_parse.cc:4993
T@3:  2105:           sql_profile.cc:        365: | | | | >PROFILING::status_change
T@3:  2106:           sql_profile.cc:        371: | | | | <PROFILING::status_change 371
T@3:  2107:             sql_audit.cc:       1111: | | | | >mysql_audit_acquire_plugins
T@3:  2108:             sql_audit.cc:       1149: | | | | <mysql_audit_acquire_plugins 1149
T@3:  2109:           transaction.cc:        445: | | | | >trans_commit_stmt
T@3:  2110:           transaction.cc:        293: | | | | | debug: add_unsafe_rollback_flags: 0
T@3:  2111:               handler.cc:       1597: | | | | | >commit_owned_gtids(...)
T@3:  2112:               handler.cc:       1626: | | | | | <commit_owned_gtids(...) 1626
T@3:  2113:               handler.cc:       1718: | | | | | >ha_commit_trans
T@3:  2114:               handler.cc:       1721: | | | | | | info: all=0 thd->in_sub_stmt=0 ha_info=0x7fd254002338 is_real_trans=1
T@3:  2115:                binlog.cc:       8639: | | | | | | >MYSQL_BIN_LOG::commit
T@3:  2116:                binlog.cc:       8641: | | | | | | | info: query='select case when  age IS NOT NULL THEN age else 33 end,  sum(age) from (  select * from t1_innodb) ta'
T@3:  2117:                binlog.cc:       8651: | | | | | | | enter: thd: 0x7fd254000e10, all: no, xid: 6, cache_mngr: 0x0
T@3:  2118:               handler.cc:       1905: | | | | | | | >ha_commit_low
T@3:  2119:             ha_innodb.cc:       4379: | | | | | | | | >innobase_commit
T@3:  2120:             ha_innodb.cc:       4381: | | | | | | | | | trans: ending transaction
T@3:  2121:             ha_innodb.cc:       2618: | | | | | | | | | >innobase_trx_init
T@3:  2122:             ha_innodb.cc:       2628: | | | | | | | | | <innobase_trx_init 2628
T@3:  2123:             ha_innodb.cc:       4515: | | | | | | | | <innobase_commit 4515
T@3:  2124:       transaction_info.h:         97: | | | | | | | | >Ha_trx_info::reset
T@3:  2125:       transaction_info.h:        101: | | | | | | | | <Ha_trx_info::reset 101
T@3:  2126:               handler.cc:       1966: | | | | | | | <ha_commit_low 1966
T@3:  2127:                binlog.cc:       8661: | | | | | | <MYSQL_BIN_LOG::commit 8661
T@3:  2128:       transaction_info.h:        409: | | | | | | >Transaction_ctx::cleanup
T@3:  2129:   rpl_transaction_ctx.cc:         40: | | | | | | | >Rpl_transaction_ctx::cleanup
T@3:  2130:   rpl_transaction_ctx.cc:         47: | | | | | | | <Rpl_transaction_ctx::cleanup 47
T@3:  2131:               my_alloc.c:        445: | | | | | | | >free_root
T@3:  2132:               my_alloc.c:        446: | | | | | | | | enter: root: 0x7fd2540048b0  flags: 1
T@3:  2133:               my_alloc.c:        489: | | | | | | | <free_root 489
T@3:  2134:       transaction_info.h:        416: | | | | | | <Transaction_ctx::cleanup 416
T@3:  2135:               handler.cc:       1875: | | | | | <ha_commit_trans 1875
T@3:  2136:           rpl_context.cc:         65: | | | | | >Rpl_consistency_ctx::notify_after_transaction_commit
T@3:  2137:           rpl_context.cc:         70: | | | | | <Rpl_consistency_ctx::notify_after_transaction_commit 70
T@3:  2138:           transaction.cc:        298: | | | | | debug: reset_unsafe_rollback_flags
T@3:  2139:           transaction.cc:        481: | | | | <trans_commit_stmt 481
T@3:  2140:             sql_union.cc:        920: | | | | >st_select_lex_unit::cleanup
T@3:  2141:             sql_union.cc:       1078: | | | | | >st_select_lex::cleanup()
T@3:  2142:            sql_select.cc:        939: | | | | | | >JOIN::destroy
T@3:  2143:              filesort.cc:        591: | | | | | | | >filesort_free_buffers
T@3:  2144:              filesort.cc:        604: | | | | | | | <filesort_free_buffers 604
T@3:  2145:             ha_innodb.cc:       2887: | | | | | | | >ha_innobase::update_thd
T@3:  2146:             ha_innodb.cc:       2889: | | | | | | | | ha_innobase::update_thd: user_thd: 0x7fd254000e10 -> 0x7fd254000e10
T@3:  2147:             ha_innodb.cc:       2618: | | | | | | | | >innobase_trx_init
T@3:  2148:             ha_innodb.cc:       2628: | | | | | | | | <innobase_trx_init 2628
T@3:  2149:             ha_innodb.cc:       2913: | | | | | | | <ha_innobase::update_thd 2913
T@3:  2150:              sql_base.cc:       1147: | | | | | | | >free_io_cache
T@3:  2151:              sql_base.cc:       1154: | | | | | | | <free_io_cache 1154
T@3:  2152:              filesort.cc:        591: | | | | | | | >filesort_free_buffers
T@3:  2153:              filesort.cc:        604: | | | | | | | <filesort_free_buffers 604
T@3:  2154:                  item.cc:        798: | | | | | | | >Item::cleanup
T@3:  2155:                  item.cc:        804: | | | | | | | <Item::cleanup 804
T@3:  2156:                  item.cc:      10834: | | | | | | | >Item_result_field::cleanup()
T@3:  2157:                  item.cc:        798: | | | | | | | | >Item::cleanup
T@3:  2158:                  item.cc:        804: | | | | | | | | <Item::cleanup 804
T@3:  2159:                  item.cc:      10837: | | | | | | | <Item_result_field::cleanup() 10837
T@3:  2160:            sql_select.cc:        986: | | | | | | <JOIN::destroy 986
T@3:  2161:             sql_union.cc:       1100: | | | | | <st_select_lex::cleanup() 1100
T@3:  2162:             sql_union.cc:        952: | | | | <st_select_lex_unit::cleanup 952
T@3:  2163:             sql_parse.cc:        741: | | | | THD::enter_stage: 'closing tables' /root/work/stonedb-dev-20230605/sql/sql_parse.cc:5052
T@3:  2164:           sql_profile.cc:        365: | | | | >PROFILING::status_change
T@3:  2165:           sql_profile.cc:        371: | | | | <PROFILING::status_change 371
T@3:  2166:              sql_base.cc:       1583: | | | | >close_thread_tables
T@3:  2167:              sql_base.cc:       1607: | | | | | tcache: table: 't1_innodb'  query_id: 0
T@3:  2168:             ha_innodb.cc:       2887: | | | | | >ha_innobase::update_thd
T@3:  2169:             ha_innodb.cc:       2889: | | | | | | ha_innobase::update_thd: user_thd: 0x7fd254000e10 -> 0x7fd254000e10
T@3:  2170:             ha_innodb.cc:       2618: | | | | | | >innobase_trx_init
T@3:  2171:             ha_innodb.cc:       2628: | | | | | | <innobase_trx_init 2628
T@3:  2172:             ha_innodb.cc:       2913: | | | | | <ha_innobase::update_thd 2913
T@3:  2173:                binlog.cc:      11975: | | | | | >THD::binlog_flush_pending_rows_event
T@3:  2174:                binlog.cc:      12001: | | | | | <THD::binlog_flush_pending_rows_event 12001
T@3:  2175:                binlog.cc:      11975: | | | | | >THD::binlog_flush_pending_rows_event
T@3:  2176:                binlog.cc:      12001: | | | | | <THD::binlog_flush_pending_rows_event 12001
T@3:  2177:                  lock.cc:        419: | | | | | >mysql_unlock_tables
T@3:  2178:              my_malloc.c:        297: | | | | | | >my_free
T@3:  2179:              my_malloc.c:        298: | | | | | | | my: ptr: 0x7fd254012370
T@3:  2180:              my_malloc.c:        304: | | | | | | <my_free 304
T@3:  2181:                  lock.cc:        425: | | | | | <mysql_unlock_tables 425
T@3:  2182:              sql_base.cc:       1468: | | | | | info: thd->open_tables: 0x7fd2540150c0
T@3:  2183:              sql_base.cc:       1758: | | | | | >close_thread_table
T@3:  2184:             ha_innodb.cc:       2887: | | | | | | >ha_innobase::update_thd
T@3:  2185:             ha_innodb.cc:       2889: | | | | | | | ha_innobase::update_thd: user_thd: 0x7fd254000e10 -> 0x7fd254000e10
T@3:  2186:             ha_innodb.cc:       2618: | | | | | | | >innobase_trx_init
T@3:  2187:             ha_innodb.cc:       2628: | | | | | | | <innobase_trx_init 2628
T@3:  2188:             ha_innodb.cc:       2913: | | | | | | <ha_innobase::update_thd 2913
T@3:  2189:               handler.cc:       8146: | | | | | | >handler::ha_reset
T@3:  2190:              sql_base.cc:       1147: | | | | | | | >free_io_cache
T@3:  2191:              sql_base.cc:       1154: | | | | | | | <free_io_cache 1154
T@3:  2192:               handler.cc:       6776: | | | | | | | >DsMrr_impl::reset
T@3:  2193:               handler.cc:       6788: | | | | | | | <DsMrr_impl::reset 6788
T@3:  2194:               handler.cc:       8165: | | | | | | <handler::ha_reset 8165
T@3:  2195:              sql_base.cc:       1805: | | | | | <close_thread_table 1805
T@3:  2196:              sql_base.cc:       1749: | | | | <close_thread_tables 1749
T@3:  2197:             sql_parse.cc:        278: | | | | >stmt_causes_implicit_commit
T@3:  2198:             sql_parse.cc:        281: | | | | <stmt_causes_implicit_commit 281
T@3:  2199:                   mdl.cc:       4720: | | | | >MDL_context::release_transactional_locks
T@3:  2200:                   mdl.cc:       4441: | | | | | >MDL_context::release_locks_stored_before
T@3:  2201:                   mdl.cc:       4444: | | | | | <MDL_context::release_locks_stored_before 4444
T@3:  2202:                   mdl.cc:       4441: | | | | | >MDL_context::release_locks_stored_before
T@3:  2203:                   mdl.cc:       4448: | | | | | | info: found lock to release ticket=0x7fd254012730
T@3:  2204:                   mdl.cc:       4288: | | | | | | >MDL_context::release_lock
T@3:  2205:                   mdl.cc:       4290: | | | | | | | enter: db=test name=t1_innodb
T@3:  2206:                   mdl.cc:       4404: | | | | | | <MDL_context::release_lock 4404
T@3:  2207:                   mdl.cc:       4452: | | | | | <MDL_context::release_locks_stored_before 4452
T@3:  2208:                   mdl.cc:       4723: | | | | <MDL_context::release_transactional_locks 4723
T@3:  2209:             sql_parse.cc:       2393: | | | | >binlog_gtid_end_transaction
T@3:  2210:             sql_parse.cc:        278: | | | | | >stmt_causes_implicit_commit
T@3:  2211:             sql_parse.cc:        281: | | | | | <stmt_causes_implicit_commit 281
T@3:  2212:             sql_parse.cc:       2435: | | | | <binlog_gtid_end_transaction 2435
T@3:  2213:             sql_parse.cc:       5150: | | | <mysql_execute_command 5150
T@3:  2214:             sql_parse.cc:        286: | | | opt: steps: ending struct
T@3:  2215:             sql_parse.cc:        286: | | | opt: (null): ending struct
T@3:  2216:      opt_trace2server.cc:        236: | | | >~opt_trace_start
T@3:  2217:              my_malloc.c:        187: | | | | >my_raw_malloc
T@3:  2218:              my_malloc.c:        188: | | | | | my: size: 40  my_flags: 16
T@3:  2219:              my_malloc.c:        230: | | | | | exit: ptr: 0x7fd254027bd0
T@3:  2220:              my_malloc.c:        231: | | | | <my_raw_malloc 231
Complete optimizer trace:
T@3:  2221:             opt_trace.cc:       1155: | | | | >Opt_trace_context::purge_stmts
T@3:  2222:              my_malloc.c:        297: | | | | | >my_free
T@3:  2223:              my_malloc.c:        298: | | | | | | my: ptr: 0x7fd254027bd0
T@3:  2224:              my_malloc.c:        304: | | | | | <my_free 304
T@3:  2225:             opt_trace.cc:       1246: | | | | <Opt_trace_context::purge_stmts 1246
T@3:  2226:      opt_trace2server.cc:        239: | | | <~opt_trace_start 239
T@3:  2227:             sql_parse.cc:        741: | | | THD::enter_stage: 'freeing items' /root/work/stonedb-dev-20230605/sql/sql_parse.cc:5680
T@3:  2228:           sql_profile.cc:        365: | | | >PROFILING::status_change
T@3:  2229:           sql_profile.cc:        371: | | | <PROFILING::status_change 371
T@3:  2230:               sql_lex.cc:        528: | | | >lex_end
T@3:  2231:               sql_lex.cc:        529: | | | | enter: lex: 0x7fd254003138
T@3:  2232:            sql_plugin.cc:       1203: | | | | >plugin_unlock_list
T@3:  2233:            sql_plugin.cc:       1132: | | | | | >intern_plugin_unlock
T@3:  2234:              my_malloc.c:        297: | | | | | | >my_free
T@3:  2235:              my_malloc.c:        298: | | | | | | | my: ptr: 0x7fd254007b20
T@3:  2236:              my_malloc.c:        304: | | | | | | <my_free 304
T@3:  2237:            sql_plugin.cc:       1149: | | | | | | info: unlocking plugin, name= InnoDB, ref_count= 18
T@3:  2238:            sql_plugin.cc:       1177: | | | | | <intern_plugin_unlock 1177
T@3:  2239:            sql_plugin.cc:       1132: | | | | | >intern_plugin_unlock
T@3:  2240:              my_malloc.c:        297: | | | | | | >my_free
T@3:  2241:              my_malloc.c:        298: | | | | | | | my: ptr: 0x7fd2540115e0
T@3:  2242:              my_malloc.c:        304: | | | | | | <my_free 304
T@3:  2243:            sql_plugin.cc:       1149: | | | | | | info: unlocking plugin, name= InnoDB, ref_count= 17
T@3:  2244:            sql_plugin.cc:       1177: | | | | | <intern_plugin_unlock 1177
T@3:  2245:            sql_plugin.cc:       1218: | | | | <plugin_unlock_list 1218
T@3:  2246:               sql_lex.cc:        537: | | | <lex_end 537
T@3:  2247:             sql_class.cc:       3412: | | | >Query_arena::free_items
T@3:  2248:                  item.cc:        798: | | | | >Item::cleanup
T@3:  2249:                  item.cc:        804: | | | | <Item::cleanup 804
T@3:  2250:                  item.cc:       8287: | | | | >Item_ref::cleanup
T@3:  2251:                  item.cc:        944: | | | | | >Item_ident::cleanup
T@3:  2252:                  item.cc:        798: | | | | | | >Item::cleanup
T@3:  2253:                  item.cc:        804: | | | | | | <Item::cleanup 804
T@3:  2254:                  item.cc:        949: | | | | | <Item_ident::cleanup 949
T@3:  2255:                  item.cc:       8292: | | | | <Item_ref::cleanup 8292
T@3:  2256:                  item.cc:       8287: | | | | >Item_ref::cleanup
T@3:  2257:                  item.cc:        944: | | | | | >Item_ident::cleanup
T@3:  2258:                  item.cc:        798: | | | | | | >Item::cleanup
T@3:  2259:                  item.cc:        804: | | | | | | <Item::cleanup 804
T@3:  2260:                  item.cc:        949: | | | | | <Item_ident::cleanup 949
T@3:  2261:                  item.cc:       8292: | | | | <Item_ref::cleanup 8292
T@3:  2262:                  item.cc:       8287: | | | | >Item_ref::cleanup
T@3:  2263:                  item.cc:        944: | | | | | >Item_ident::cleanup
T@3:  2264:                  item.cc:        798: | | | | | | >Item::cleanup
T@3:  2265:                  item.cc:        804: | | | | | | <Item::cleanup 804
T@3:  2266:                  item.cc:        949: | | | | | <Item_ident::cleanup 949
T@3:  2267:                  item.cc:       8292: | | | | <Item_ref::cleanup 8292
T@3:  2268:                  item.cc:       6071: | | | | >Item_field::cleanup
T@3:  2269:                  item.cc:        944: | | | | | >Item_ident::cleanup
T@3:  2270:                  item.cc:        798: | | | | | | >Item::cleanup
T@3:  2271:                  item.cc:        804: | | | | | | <Item::cleanup 804
T@3:  2272:                  item.cc:        949: | | | | | <Item_ident::cleanup 949
T@3:  2273:                  item.cc:       6082: | | | | <Item_field::cleanup 6082
T@3:  2274:                  item.cc:       6071: | | | | >Item_field::cleanup
T@3:  2275:                  item.cc:        944: | | | | | >Item_ident::cleanup
T@3:  2276:                  item.cc:        798: | | | | | | >Item::cleanup
T@3:  2277:                  item.cc:        804: | | | | | | <Item::cleanup 804
T@3:  2278:                  item.cc:        949: | | | | | <Item_ident::cleanup 949
T@3:  2279:                  item.cc:       6082: | | | | <Item_field::cleanup 6082
T@3:  2280:                  item.cc:       6071: | | | | >Item_field::cleanup
T@3:  2281:                  item.cc:        944: | | | | | >Item_ident::cleanup
T@3:  2282:                  item.cc:        798: | | | | | | >Item::cleanup
T@3:  2283:                  item.cc:        804: | | | | | | <Item::cleanup 804
T@3:  2284:                  item.cc:        949: | | | | | <Item_ident::cleanup 949
T@3:  2285:                  item.cc:       6082: | | | | <Item_field::cleanup 6082
T@3:  2286:                  item.cc:        798: | | | | >Item::cleanup
T@3:  2287:                  item.cc:        804: | | | | <Item::cleanup 804
T@3:  2288:                  item.cc:        798: | | | | >Item::cleanup
T@3:  2289:                  item.cc:        804: | | | | <Item::cleanup 804
T@3:  2290:                  item.cc:      10834: | | | | >Item_result_field::cleanup()
T@3:  2291:                  item.cc:        798: | | | | | >Item::cleanup
T@3:  2292:                  item.cc:        804: | | | | | <Item::cleanup 804
T@3:  2293:                  item.cc:      10837: | | | | <Item_result_field::cleanup() 10837
T@3:  2294:                  item.cc:        798: | | | | >Item::cleanup
T@3:  2295:                  item.cc:        804: | | | | <Item::cleanup 804
T@3:  2296:                  item.cc:       6071: | | | | >Item_field::cleanup
T@3:  2297:                  item.cc:        944: | | | | | >Item_ident::cleanup
T@3:  2298:                  item.cc:        798: | | | | | | >Item::cleanup
T@3:  2299:                  item.cc:        804: | | | | | | <Item::cleanup 804
T@3:  2300:                  item.cc:        949: | | | | | <Item_ident::cleanup 949
T@3:  2301:                  item.cc:       6082: | | | | <Item_field::cleanup 6082
T@3:  2302:                  item.cc:        798: | | | | >Item::cleanup
T@3:  2303:                  item.cc:        804: | | | | <Item::cleanup 804
T@3:  2304:                  item.cc:       6071: | | | | >Item_field::cleanup
T@3:  2305:                  item.cc:        944: | | | | | >Item_ident::cleanup
T@3:  2306:                  item.cc:        798: | | | | | | >Item::cleanup
T@3:  2307:                  item.cc:        804: | | | | | | <Item::cleanup 804
T@3:  2308:                  item.cc:        949: | | | | | <Item_ident::cleanup 949
T@3:  2309:                  item.cc:       6082: | | | | <Item_field::cleanup 6082
T@3:  2310:                  item.cc:        798: | | | | >Item::cleanup
T@3:  2311:                  item.cc:        804: | | | | <Item::cleanup 804
T@3:  2312:                  item.cc:      10834: | | | | >Item_result_field::cleanup()
T@3:  2313:                  item.cc:        798: | | | | | >Item::cleanup
T@3:  2314:                  item.cc:        804: | | | | | <Item::cleanup 804
T@3:  2315:                  item.cc:      10837: | | | | <Item_result_field::cleanup() 10837
T@3:  2316:          item_cmpfunc.cc:       4243: | | | | >Item_func_case::cleanup
T@3:  2317:                  item.cc:      10834: | | | | | >Item_result_field::cleanup()
T@3:  2318:                  item.cc:        798: | | | | | | >Item::cleanup
T@3:  2319:                  item.cc:        804: | | | | | | <Item::cleanup 804
T@3:  2320:                  item.cc:      10837: | | | | | <Item_result_field::cleanup() 10837
T@3:  2321:          item_cmpfunc.cc:       4250: | | | | <Item_func_case::cleanup 4250

思路分析:

  1. 该debug的trace日志包含这个查询SQL的所有关键函数的执行过程
  2. 从这个日志中可以分析出mysql/sql层所有的查询优化和查询执行的细节
  3. 但是将问题聚焦下, 当前的目标是解决tianmu查询出错, 那么对应的就需要关注mysql/sql在处理case属性时是如何处理的
  4. 也就是说,要将重点,放在mysql/sql层是如何处理case的item的
  5. 这里需要注意, 分析case的item并不是为了要记住这块代码的处理逻辑, 而是作为一个正确处理case的解决方案的对标, 分析tianmu引擎在处理case的属性时的逻辑,从而有个逻辑处理的对比
  6. tianmu引擎对于case的属性有自己的一套处理逻辑, 在此处分析的一个歧途就是容易将mysql/sql当作唯一正确的接, 从而照抄mysql/sql的方案, 而忽略了tianmu引擎对于列属性处理的特殊性, 此特殊的地方在以下分析tianmu引擎对查询执行时具体分析

mysql/sql层的核心处理:

Item_func_case::fix_fields

调用堆栈:

#0  Item_func_case::fix_fields (this=0x7fd254005850, thd=0x7fd254000e10, ref=0x7fd254005ac0) at /root/work/stonedb-dev-20230605/sql/item_cmpfunc.cc:4001
#1  0x00000000023a8a7e in setup_fields (thd=0x7fd254000e10, ref_pointer_array=..., fields=..., want_privilege=1, sum_func_list=0x7fd254004d30, allow_sum_func=true, column_update=false)
    at /root/work/stonedb-dev-20230605/sql/sql_base.cc:9138
#2  0x00000000024650da in st_select_lex::prepare (this=0x7fd254004bd0, thd=0x7fd254000e10) at /root/work/stonedb-dev-20230605/sql/sql_resolver.cc:200
#3  0x000000000247165f in handle_query (thd=0x7fd254000e10, lex=0x7fd254003138, result=0x7fd254006a88, added_options=0, removed_options=0, is_optimize_after_tianmu=0, free_join_from_tianmu=0)
    at /root/work/stonedb-dev-20230605/sql/sql_select.cc:139
#4  0x0000000002427ae2 in execute_sqlcom_select (thd=0x7fd254000e10, all_tables=0x7fd254014a88) at /root/work/stonedb-dev-20230605/sql/sql_parse.cc:5205
#5  0x0000000002420e1e in mysql_execute_command (thd=0x7fd254000e10, first_level=true) at /root/work/stonedb-dev-20230605/sql/sql_parse.cc:2847
#6  0x0000000002428b0d in mysql_parse (thd=0x7fd254000e10, parser_state=0x7fd519f6ff90) at /root/work/stonedb-dev-20230605/sql/sql_parse.cc:5642
#7  0x000000000241db04 in dispatch_command (thd=0x7fd254000e10, com_data=0x7fd519f70730, command=COM_QUERY) at /root/work/stonedb-dev-20230605/sql/sql_parse.cc:1495
#8  0x000000000241c945 in do_command (thd=0x7fd254000e10) at /root/work/stonedb-dev-20230605/sql/sql_parse.cc:1034
#9  0x000000000254eeb5 in handle_connection (arg=0x965db20) at /root/work/stonedb-dev-20230605/sql/conn_handler/connection_handler_per_thread.cc:313
#10 0x0000000002c1e6f4 in pfs_spawn_thread (arg=0x9490860) at /root/work/stonedb-dev-20230605/storage/perfschema/pfs.cc:2197
#11 0x00007fd569c2b1ca in start_thread () from /lib64/libpthread.so.0
#12 0x00007fd566aa1e73 in clone () from /lib64/libc.so.6

函数实现:

bool Item_func_case::fix_fields(THD *thd, Item **ref)
{
  /*
    buff should match stack usage from
    Item_func_case::val_int() -> Item_func_case::find_item()
  */
  uchar buff[MAX_FIELD_WIDTH*2+sizeof(String)*2+sizeof(String*)*2+sizeof(double)*2+sizeof(longlong)*2];
  bool res= Item_func::fix_fields(thd, ref);
  /*
    Call check_stack_overrun after fix_fields to be sure that stack variable
    is not optimized away
  */
  if (check_stack_overrun(thd, STACK_MIN_SIZE, buff))
    return TRUE;				// Fatal error flag is set!
  return res;
}

参数ref的数据:

(gdb) p ref[0][0]
$4 = (Item_func_case) {
  <Item_func> = {
    <Item_result_field> = {
      <Item> = {
        <Parse_tree_node> = {
          _vptr.Parse_tree_node = 0x4355418 <vtable for Item_func_case+16>, 
          contextualized = true, 
          transitional = false
        }, 
        members of Item: 
        is_expensive_cache = -1 '\377', 
        rsize = 0, 
        str_value = {
          m_ptr = 0x0, 
          m_length = 0, 
          m_charset = 0x44664e0 <my_charset_bin>, 
          m_alloced_length = 0, 
          m_is_alloced = false
        }, 
        item_name = {
          <Name_string> = {
            <Simple_cstring> = {
              m_str = 0x7fd2540065e0 "case when  age IS NOT NULL THEN age else 33 end", 
              m_length = 47
            }, <No data fields>}, 
          members of Item_name_string: 
          m_is_autogenerated = true
        }, 
        orig_name = {
          <Name_string> = {
            <Simple_cstring> = {
              m_str = 0x0, 
              m_length = 0
            }, <No data fields>}, 
          members of Item_name_string: 
          m_is_autogenerated = true
        }, 
        next = 0x7fd2540059d0, 
        max_length = 0, 
        marker = 0, 
        decimals = 0 '\000', 
        maybe_null = 0 '\000', 
        null_value = 0 '\000', 
        unsigned_flag = 0 '\000', 
        with_sum_func = 0 '\000', 
        fixed = 0 '\000', 
        collation = {
          collation = 0x44664e0 <my_charset_bin>, 
--Type <RET> for more, q to quit, c to continue without paging--
          derivation = DERIVATION_COERCIBLE, 
          repertoire = 3
        }, 
        cmp_context = 4294967295, 
        runtime_item = false, 
        derived_used = false, 
        with_subselect = 0 '\000', 
        with_stored_program = 0 '\000', 
        tables_locked_cache = false, 
        is_parser_item = true
      }, 
      members of Item_result_field: 
      result_field = 0x0
    }, 
    members of Item_func: 
    args = 0x7fd2540059b8, 
    tmp_arg = {0x8f8f8f8f8f8f8f8f, 0x8f8f8f8f8f8f8f8f}, 
    const_item_cache = 143, 
    allowed_arg_cols = 1, 
    used_tables_cache = 10344644715844964239, 
    not_null_tables_cache = 10344644715844964239, 
    arg_count = 3
  }, 
  members of Item_func_case: 
  first_expr_num = -1, 
  else_expr_num = 2, 
  cached_result_type = INT_RESULT, 
  left_result_type = INT_RESULT, 
  tmp_value = {
    m_ptr = 0x0, 
    m_length = 0, 
    m_charset = 0x44664e0 <my_charset_bin>, 
    m_alloced_length = 0, 
    m_is_alloced = false
  }, 
  ncases = 2, 
  cmp_type = 2408550287, 
  cmp_collation = {
    collation = 0x44664e0 <my_charset_bin>, 
    derivation = DERIVATION_NONE, 
    repertoire = 3
  }, 
  cached_field_type = 2408550287, 
  cmp_items = {0x0, 0x0, 0x0, 0x0, 0x0}, 
  case_item = 0x0
}

(gdb) p ( (Item_func*) ref[0]).args[0][0]
$8 = (Item_func_isnotnull) {
  <Item_bool_func> = {
    <Item_int_func> = {
      <Item_func> = {
        <Item_result_field> = {
          <Item> = {
            <Parse_tree_node> = {
              _vptr.Parse_tree_node = 0x4351ba0 <vtable for Item_func_isnotnull+16>, 
              contextualized = true, 
              transitional = false
            }, 
            members of Item: 
            is_expensive_cache = -1 '\377', 
            rsize = 0, 
            str_value = {
              m_ptr = 0x0, 
              m_length = 0, 
              m_charset = 0x44664e0 <my_charset_bin>, 
              m_alloced_length = 0, 
              m_is_alloced = false
            }, 
            item_name = {
              <Name_string> = {
                <Simple_cstring> = {
                  m_str = 0x0, 
                  m_length = 0
                }, <No data fields>}, 
              members of Item_name_string: 
              m_is_autogenerated = true
            }, 
            orig_name = {
              <Name_string> = {
                <Simple_cstring> = {
                  m_str = 0x0, 
                  m_length = 0
                }, <No data fields>}, 
              members of Item_name_string: 
              m_is_autogenerated = true
            }, 
            next = 0x7fd254005850, 
            max_length = 21, 
            marker = 0, 
            decimals = 0 '\000', 
            maybe_null = 0 '\000', 
            null_value = 0 '\000', 
            unsigned_flag = 0 '\000', 
            with_sum_func = 0 '\000', 
            fixed = 0 '\000', 
--Type <RET> for more, q to quit, c to continue without paging--
            collation = {
              collation = 0x446eae0 <my_charset_latin1>, 
              derivation = DERIVATION_NUMERIC, 
              repertoire = 1
            }, 
            cmp_context = 4294967295, 
            runtime_item = false, 
            derived_used = false, 
            with_subselect = 0 '\000', 
            with_stored_program = 0 '\000', 
            tables_locked_cache = false, 
            is_parser_item = true
          }, 
          members of Item_result_field: 
          result_field = 0x0
        }, 
        members of Item_func: 
        args = 0x7fd254005678, 
        tmp_arg = {0x7fd2540063a0, 0x8f8f8f8f8f8f8f8f}, 
        const_item_cache = 143, 
        allowed_arg_cols = 1, 
        used_tables_cache = 10344644715844964239, 
        not_null_tables_cache = 10344644715844964239, 
        arg_count = 1
      }, <No data fields>}, 
    members of Item_bool_func: 
    m_created_by_in2exists = false
  }, 
  members of Item_func_isnotnull: 
  abort_on_null = false
}

(gdb) p ( (Item_func*) ref[0]).args[1][0]
$9 = (Item_field) {
  <Item_ident> = {
    <Item> = {
      <Parse_tree_node> = {
        _vptr.Parse_tree_node = 0x43485f8 <vtable for Item_field+16>, 
        contextualized = true, 
        transitional = false
      }, 
      members of Item: 
      is_expensive_cache = -1 '\377', 
      rsize = 0, 
      str_value = {
        m_ptr = 0x0, 
        m_length = 0, 
        m_charset = 0x44664e0 <my_charset_bin>, 
        m_alloced_length = 0, 
        m_is_alloced = false
      }, 
      item_name = {
        <Name_string> = {
          <Simple_cstring> = {
            m_str = 0x7fd2540056a8 "age", 
            m_length = 3
          }, <No data fields>}, 
        members of Item_name_string: 
        m_is_autogenerated = true
      }, 
      orig_name = {
        <Name_string> = {
          <Simple_cstring> = {
            m_str = 0x0, 
            m_length = 0
          }, <No data fields>}, 
        members of Item_name_string: 
        m_is_autogenerated = true
      }, 
      next = 0x7fd2540056b0, 
      max_length = 0, 
      marker = 0, 
      decimals = 0 '\000', 
      maybe_null = 0 '\000', 
      null_value = 0 '\000', 
      unsigned_flag = 0 '\000', 
      with_sum_func = 0 '\000', 
      fixed = 0 '\000', 
      collation = {
        collation = 0x44664e0 <my_charset_bin>, 
        derivation = DERIVATION_IMPLICIT, 
--Type <RET> for more, q to quit, c to continue without paging--
        repertoire = 3
      }, 
      cmp_context = 4294967295, 
      runtime_item = false, 
      derived_used = false, 
      with_subselect = 0 '\000', 
      with_stored_program = 0 '\000', 
      tables_locked_cache = false, 
      is_parser_item = true
    }, 
    members of Item_ident: 
    orig_db_name = 0x0, 
    orig_table_name = 0x0, 
    orig_field_name = 0x7fd2540056a8 "age", 
    m_alias_of_expr = false, 
    context = 0x7fd254004c30, 
    db_name = 0x0, 
    table_name = 0x0, 
    field_name = 0x7fd2540056a8 "age", 
    cached_field_index = 4294967295, 
    cached_table = 0x0, 
    depended_from = 0x0
  }, 
  members of Item_field: 
  table_ref = 0x0, 
  field = 0x0, 
  result_field = 0x0, 
  item_equal = 0x0, 
  no_const_subst = false, 
  have_privileges = 0, 
  any_privileges = false
}

函数目的分析:

  1. 此处是构建case属性的相关列属性的过程, ref的参数包含两种列属性
    1. 参与case运算的 Item_func_isnotnull
    2. 表的列属性Item_field
  2. 此时尚未读取列属性Item_field中的值进行计算取值, 目的在于构建case列属性相关信息的完整结构

Item_func_case::val_int

调用堆栈:

#0  Item_func_case::val_int (this=0x7fd254005850) at /root/work/stonedb-dev-20230605/sql/item_cmpfunc.cc:3897
#1  0x0000000001dde09f in Item_copy_int::copy (this=0x7fd2540217b0, thd=0x7fd254000e10) at /root/work/stonedb-dev-20230605/sql/item.cc:4868
#2  0x00000000023e3292 in copy_fields (param=0x7fd254020ff8, thd=0x7fd254000e10) at /root/work/stonedb-dev-20230605/sql/sql_executor.cc:4381
#3  0x000000000247902f in JOIN::clear (this=0x7fd254020ec0) at /root/work/stonedb-dev-20230605/sql/sql_select.cc:3365
#4  0x00000000023dfb7b in end_send_group (join=0x7fd254020ec0, qep_tab=0x7fd254021730, end_of_records=true) at /root/work/stonedb-dev-20230605/sql/sql_executor.cc:3060
#5  0x00000000023db32a in sub_select (join=0x7fd254020ec0, qep_tab=0x7fd2540215b8, end_of_records=true) at /root/work/stonedb-dev-20230605/sql/sql_executor.cc:1233
#6  0x00000000023dae9e in do_select (join=0x7fd254020ec0) at /root/work/stonedb-dev-20230605/sql/sql_executor.cc:959
#7  0x00000000023d8ded in JOIN::exec (this=0x7fd254020ec0) at /root/work/stonedb-dev-20230605/sql/sql_executor.cc:206
#8  0x00000000024717e3 in handle_query (thd=0x7fd254000e10, lex=0x7fd254003138, result=0x7fd254006a88, added_options=0, removed_options=0, is_optimize_after_tianmu=0, free_join_from_tianmu=0)
    at /root/work/stonedb-dev-20230605/sql/sql_select.cc:195
#9  0x0000000002427ae2 in execute_sqlcom_select (thd=0x7fd254000e10, all_tables=0x7fd254014a88) at /root/work/stonedb-dev-20230605/sql/sql_parse.cc:5205
#10 0x0000000002420e1e in mysql_execute_command (thd=0x7fd254000e10, first_level=true) at /root/work/stonedb-dev-20230605/sql/sql_parse.cc:2847
#11 0x0000000002428b0d in mysql_parse (thd=0x7fd254000e10, parser_state=0x7fd519f6ff90) at /root/work/stonedb-dev-20230605/sql/sql_parse.cc:5642
#12 0x000000000241db04 in dispatch_command (thd=0x7fd254000e10, com_data=0x7fd519f70730, command=COM_QUERY) at /root/work/stonedb-dev-20230605/sql/sql_parse.cc:1495
#13 0x000000000241c945 in do_command (thd=0x7fd254000e10) at /root/work/stonedb-dev-20230605/sql/sql_parse.cc:1034
#14 0x000000000254eeb5 in handle_connection (arg=0x965db20) at /root/work/stonedb-dev-20230605/sql/conn_handler/connection_handler_per_thread.cc:313
#15 0x0000000002c1e6f4 in pfs_spawn_thread (arg=0x9490860) at /root/work/stonedb-dev-20230605/storage/perfschema/pfs.cc:2197
#16 0x00007fd569c2b1ca in start_thread () from /lib64/libpthread.so.0
#17 0x00007fd566aa1e73 in clone () from /lib64/libc.so.6

函数实现:

longlong Item_func_case::val_int()
{
  assert(fixed == 1);
  char buff[MAX_FIELD_WIDTH];
  String dummy_str(buff,sizeof(buff),default_charset());
  Item *item=find_item(&dummy_str);
  longlong res;

  if (!item)
  {
    null_value=1;
    return 0;
  }
  res=item->val_int();
  null_value=item->null_value;
  return res;
}

函数实现的运算逻辑的数据:

函数目的分析:

  1. 该函数实现了case属性的取值和计算, 代码逻辑非常简单直接,这里不做过多分析
  2. 值得注意的是该函数被调用的位置, 是在end_send_group中, 由copy_fields完成
  3. 需要注意myql/sql中的item的概念, 是作为一个基本元素来理解, 即使是一个包含case运算的列, 也是一个item. 而这个包含case运算的item又包含了其他属性的item。在当前场景下具体为 Item_func_case
  4. item对上层提供的统一抽象接口是val_int和val_str, 即使是逻辑分支运算例如case, 也是被包括在了val_int中进行

tianmu执行分析:

构建查询序列:

T:-1 = TABLE_ALIAS(T:0,"t1")
T:-2 = TMP_TABLE(T:4294967295)
VC:-2.0 = CREATE_VC(T:-2,EXPR("case"))
A:-1 = T:-2.ADD_COLUMN(VC:-2.0,LIST,"case when  age IS NOT NULL THEN age else 33 end","ALL"
VC:-2.1 = CREATE_VC(T:-2,PHYS_COL(T:-1,A:0))
A:-2 = T:-2.ADD_COLUMN(VC:-2.1,SUM,"sum(age)","ALL")
T:-2.APPLY_CONDS()
RESULT(T:-2)

tianmu的构建查询序列的分析思路:

  1. 需要注意这个日志仅仅是将mysql/sql的查询树, 转换成tianmu的查询序列的过程, 是一个构建的过程,而非执行查询序列的过程
  2. 查询序列的执行的入口是APPLY_CONDS()
  3. 查询序列的每一个操作符的具体的执行过程, 需要从APPLY_CONDS()入口开始逐步定位
  4. 具体到本问题, 就是要分析EXPR的列属性,是被如何具体处理

tianmu执行的核心处理:

MysqlExpression::EvalType

调用堆栈:

#0  Tianmu::core::MysqlExpression::EvalType (this=0x7fd254024e70, tv=0x7fd254eb5bd8) at /root/work/stonedb-dev-20230605/storage/tianmu/core/mysql_expression.cpp:373
#1  0x0000000002e406d2 in Tianmu::vcolumn::ExpressionColumn::ExpressionColumn (this=0x7fd254eb5a30, expr=0x7fd254024e70, temp_table=0x7fd254eb56f0, temp_table_alias=-2, 
    multi_index=0x7fd254026e40) at /root/work/stonedb-dev-20230605/storage/tianmu/vc/expr_column.cpp:72
#2  0x0000000002cfd078 in Tianmu::core::Query::CreateColumnFromExpression (this=0x7fd519f6e750, exprs=std::vector of length 1, capacity 1 = {...}, temp_table=0x7fd254eb56f0, 
    temp_table_alias=-2, mind=0x7fd254026e40) at /root/work/stonedb-dev-20230605/storage/tianmu/core/query.cpp:498
#3  0x0000000002cff9d5 in Tianmu::core::Query::Preexecute (this=0x7fd519f6e750, qu=..., sender=0x71c5c40, display_now=true) at /root/work/stonedb-dev-20230605/storage/tianmu/core/query.cpp:820
#4  0x0000000002cebb38 in Tianmu::core::Engine::Execute (this=0x7218d50, thd=0x7fd254000e10, lex=0x7fd254003138, result_output=0x7fd254006a68, unit_for_union=0x0)
    at /root/work/stonedb-dev-20230605/storage/tianmu/core/engine_execute.cpp:513
#5  0x0000000002cea825 in Tianmu::core::Engine::HandleSelect (this=0x7218d50, thd=0x7fd254000e10, lex=0x7fd254003138, result=@0x7fd519f6edc8: 0x7fd254006a68, setup_tables_done_option=0, 
    res=@0x7fd519f6edc4: 0, is_optimize_after_tianmu=@0x7fd519f6edbc: 1, tianmu_free_join=@0x7fd519f6edc0: 1, with_insert=0)
    at /root/work/stonedb-dev-20230605/storage/tianmu/core/engine_execute.cpp:243
#6  0x0000000003084b13 in Tianmu::DBHandler::ha_my_tianmu_query (thd=0x7fd254000e10, lex=0x7fd254003138, result_output=@0x7fd519f6edc8: 0x7fd254006a68, setup_tables_done_option=0, 
    res=@0x7fd519f6edc4: 0, is_optimize_after_tianmu=@0x7fd519f6edbc: 1, tianmu_free_join=@0x7fd519f6edc0: 1, with_insert=0)
    at /root/work/stonedb-dev-20230605/storage/tianmu/sql/ha_my_tianmu.cpp:95
#7  0x0000000002427aa8 in execute_sqlcom_select (thd=0x7fd254000e10, all_tables=0x7fd254014a88) at /root/work/stonedb-dev-20230605/sql/sql_parse.cc:5204
#8  0x0000000002420e1e in mysql_execute_command (thd=0x7fd254000e10, first_level=true) at /root/work/stonedb-dev-20230605/sql/sql_parse.cc:2847
#9  0x0000000002428b0d in mysql_parse (thd=0x7fd254000e10, parser_state=0x7fd519f6ff90) at /root/work/stonedb-dev-20230605/sql/sql_parse.cc:5642
#10 0x000000000241db04 in dispatch_command (thd=0x7fd254000e10, com_data=0x7fd519f70730, command=COM_QUERY) at /root/work/stonedb-dev-20230605/sql/sql_parse.cc:1495
#11 0x000000000241c945 in do_command (thd=0x7fd254000e10) at /root/work/stonedb-dev-20230605/sql/sql_parse.cc:1034
#12 0x000000000254eeb5 in handle_connection (arg=0x965db20) at /root/work/stonedb-dev-20230605/sql/conn_handler/connection_handler_per_thread.cc:313
#13 0x0000000002c1e6f4 in pfs_spawn_thread (arg=0x9490860) at /root/work/stonedb-dev-20230605/storage/perfschema/pfs.cc:2197
#14 0x00007fd569c2b1ca in start_thread () from /lib64/libpthread.so.0
#15 0x00007fd566aa1e73 in clone () from /lib64/libc.so.6

函数实现:

DataType MysqlExpression::EvalType(TypOfVars *tv) {
  // set types of variables (_tianmufieldsCache)
  if (tv) {
    DataType fieldtype;
    auto tianmufield_set = tianmu_fields_cache.begin();
    while (tianmufield_set != tianmu_fields_cache.end()) {
      auto it = tv->find(tianmufield_set->first);
      if (it != tv->end()) {
        for (auto &tianmufield : tianmufield_set->second) {
          fieldtype = it->second;
          tianmufield->SetType(fieldtype);
        }
      }
      tianmufield_set++;
    }
  }

函数目的分析:

  1. 最大的一个问题便是为什么要在这个函数打断点做追踪?
    1. 对expr的处理的代码并不熟悉, 不知道tianmu具体是怎么处理case的, 无法直接在逻辑处理的地方打断点, 或者说对代码的熟悉程度不足以直接定位问题。这也是现在大部分的解决问题的时候面临的难题, 对这块代码的熟悉程度不足, 是在解决这块代码的问题时才第一次理解该块逻辑的代码
    2. 但是EvalType是必定要走到的, 必须要拿到expr的类型
  2. 从这个函数的调用堆栈出发, 理解expr的属性的处理流程

MysqlExpression::GetTianmufieldItem

调用堆栈:

#0  Tianmu::core::MysqlExpression::GetTianmufieldItem (this=0x7fd254024e70, ifield=0x7fd254eb2a08) at /root/work/stonedb-dev-20230605/storage/tianmu/core/mysql_expression.cpp:165
#1  0x00000000030c5a05 in Tianmu::core::MysqlExpression::TransformTree (this=0x7fd254024e70, root=0x7fd254eb2a08, dir=Tianmu::core::MysqlExpression::TransformDirection::FORWARD)
    at /root/work/stonedb-dev-20230605/storage/tianmu/core/mysql_expression.cpp:240
#2  0x00000000030c5f09 in Tianmu::core::MysqlExpression::TransformTree (this=0x7fd254024e70, root=0x7fd254eb2f50, dir=Tianmu::core::MysqlExpression::TransformDirection::FORWARD)
    at /root/work/stonedb-dev-20230605/storage/tianmu/core/mysql_expression.cpp:333
#3  0x00000000030c5e05 in Tianmu::core::MysqlExpression::TransformTree (this=0x7fd254024e70, root=0x7fd2540055c0, dir=Tianmu::core::MysqlExpression::TransformDirection::FORWARD)
    at /root/work/stonedb-dev-20230605/storage/tianmu/core/mysql_expression.cpp:317
#4  0x00000000030c5e05 in Tianmu::core::MysqlExpression::TransformTree (this=0x7fd254024e70, root=0x7fd254005840, dir=Tianmu::core::MysqlExpression::TransformDirection::FORWARD)
    at /root/work/stonedb-dev-20230605/storage/tianmu/core/mysql_expression.cpp:317
#5  0x00000000030c4ce1 in Tianmu::core::MysqlExpression::MysqlExpression (this=0x7fd254024e70, item=0x7fd254005840, item2varid=std::map with 1 element = {...})
    at /root/work/stonedb-dev-20230605/storage/tianmu/core/mysql_expression.cpp:50
#6  0x0000000002d24303 in Tianmu::core::Query::WrapMysqlExpression (this=0x7fd519f6e750, item=0x7fd254005840, tmp_table=..., expr=@0x7fd519f6e1c8: 0x0, in_where=false, aggr_used=false)
    at /root/work/stonedb-dev-20230605/storage/tianmu/core/query_compile.cpp:834
#7  0x0000000002d22279 in Tianmu::core::Query::AddFields (this=0x7fd519f6e750, fields=..., tmp_table=..., base_table=..., group_by_clause=false, num_of_added_fields=@0x7fd519f6e4c4: 0, 
    ignore_minmax=false, aggregation_used=@0x7fd519f6e40f: false) at /root/work/stonedb-dev-20230605/storage/tianmu/core/query_compile.cpp:495
#8  0x0000000002d25efd in Tianmu::core::Query::Compile (this=0x7fd519f6e750, compiled_query=0x7fd519f6e680, selects_list=0x7fd254004bc0, last_distinct=0x0, res_tab=0x0, ignore_limit=false, 
    left_expr_for_subselect=0x0, oper_for_subselect=0x0, ignore_minmax=false, for_subq_in_where=false) at /root/work/stonedb-dev-20230605/storage/tianmu/core/query_compile.cpp:1210
#9  0x0000000002ceb69c in Tianmu::core::Engine::Execute (this=0x7218d50, thd=0x7fd254000e10, lex=0x7fd254003138, result_output=0x7fd254006a68, unit_for_union=0x0)
    at /root/work/stonedb-dev-20230605/storage/tianmu/core/engine_execute.cpp:472
#10 0x0000000002cea825 in Tianmu::core::Engine::HandleSelect (this=0x7218d50, thd=0x7fd254000e10, lex=0x7fd254003138, result=@0x7fd519f6edc8: 0x7fd254006a68, setup_tables_done_option=0, 
    res=@0x7fd519f6edc4: 0, is_optimize_after_tianmu=@0x7fd519f6edbc: 1, tianmu_free_join=@0x7fd519f6edc0: 1, with_insert=0)
    at /root/work/stonedb-dev-20230605/storage/tianmu/core/engine_execute.cpp:243
#11 0x0000000003084b13 in Tianmu::DBHandler::ha_my_tianmu_query (thd=0x7fd254000e10, lex=0x7fd254003138, result_output=@0x7fd519f6edc8: 0x7fd254006a68, setup_tables_done_option=0, 
    res=@0x7fd519f6edc4: 0, is_optimize_after_tianmu=@0x7fd519f6edbc: 1, tianmu_free_join=@0x7fd519f6edc0: 1, with_insert=0)
    at /root/work/stonedb-dev-20230605/storage/tianmu/sql/ha_my_tianmu.cpp:95
#12 0x0000000002427aa8 in execute_sqlcom_select (thd=0x7fd254000e10, all_tables=0x7fd254014a88) at /root/work/stonedb-dev-20230605/sql/sql_parse.cc:5204
#13 0x0000000002420e1e in mysql_execute_command (thd=0x7fd254000e10, first_level=true) at /root/work/stonedb-dev-20230605/sql/sql_parse.cc:2847
#14 0x0000000002428b0d in mysql_parse (thd=0x7fd254000e10, parser_state=0x7fd519f6ff90) at /root/work/stonedb-dev-20230605/sql/sql_parse.cc:5642
#15 0x000000000241db04 in dispatch_command (thd=0x7fd254000e10, com_data=0x7fd519f70730, command=COM_QUERY) at /root/work/stonedb-dev-20230605/sql/sql_parse.cc:1495
#16 0x000000000241c945 in do_command (thd=0x7fd254000e10) at /root/work/stonedb-dev-20230605/sql/sql_parse.cc:1034
#17 0x000000000254eeb5 in handle_connection (arg=0x965db20) at /root/work/stonedb-dev-20230605/sql/conn_handler/connection_handler_per_thread.cc:313
#18 0x0000000002c1e6f4 in pfs_spawn_thread (arg=0x9490860) at /root/work/stonedb-dev-20230605/storage/perfschema/pfs.cc:2197
#19 0x00007fd569c2b1ca in start_thread () from /lib64/libpthread.so.0
#20 0x00007fd566aa1e73 in clone () from /lib64/libc.so.6

函数实现:

Item_tianmufield *MysqlExpression::GetTianmufieldItem(Item_field *ifield) {
  auto key = item2varid->find(ifield);
  DEBUG_ASSERT(key != item2varid->end());
  auto it = tianmu_fields_cache.find(key->second);
  Item_tianmufield *tianmufield = nullptr;
  if (it != tianmu_fields_cache.end()) {
    tianmufield = *it->second.begin();
    tianmufield->varID.push_back(key->second);
  } else {
    tianmufield = new Item_tianmufield(ifield, key->second);
    std::set<Item_tianmufield *> s_tmp;
    s_tmp.insert(tianmufield);
    tianmu_fields_cache.insert(make_pair(key->second, s_tmp));
  }
  return (tianmufield);
}

函数目的分析:

  1. 由于上层逻辑是在执行Query::AddFields和TransformTree, 所以不能从GetTianmufieldItem中获取具体的业务处理相关的信息
  2. 那么为什么还要在这个函数挂断点分析执行过程呢? 因为这个函数是在处理Item_tianmufield的其中一个过程

Tianmu::core::Query::Preexecute

执行结果的临时表的case里属性的数据:

(gdb) p output_table[0].attrs[0][0]
$27 = (Tianmu::core::TempTable::Attr) {
  <Tianmu::core::PhysicalColumn> = {
    <Tianmu::core::Column> = {
      ct = {
        type = Tianmu::common::ColumnType::BIGINT, 
        unsigned_flag_ = false, 
        precision = 19, 
        scale = 0, 
        internal_size = 8, 
        display_size = 20, 
        collation = {
          collation = 0x44664e0 <my_charset_bin>, 
          derivation = DERIVATION_NONE, 
          repertoire = 3
        }, 
        fmt = Tianmu::common::PackFmt::DEFAULT, 
        flag = std::bitset
      }
    }, 
    members of Tianmu::core::PhysicalColumn: 
    _vptr.PhysicalColumn = 0x44057d0 <vtable for Tianmu::core::TempTable::Attr+16>, 
    is_unique = false, 
    is_unique_updated = false
  }, 
  members of Tianmu::core::TempTable::Attr: 
  si = {
    separator = "", 
    order = st_order::ORDER_NOT_RELEVANT
  }, 
  buffer = 0x0, 
  no_obj = 0, 
  no_power = 16, 
  no_materialized = 0, 
  page_size = 1, 
  alias = 0x7fd254024a70 "case when  age IS NOT NULL THEN age else 33 end", 
  mode = Tianmu::common::ColOperation::LISTING, 
  distinct = false, 
  term = {
    type = Tianmu::common::ColumnType::UNK, 
    vc = 0x7fd254eb5a30, 
    cond_value = std::vector of length 0, capacity 0, 
    cond_numvalue = std::shared_ptr<Tianmu::utils::Hash64> (empty) = {
      get() = 0x0
    }, 
    vc_id = 0, 
    is_vc_owner = false, 
    item = 0x0
  }, 
--Type <RET> for more, q to quit, c to continue without paging--
  dim = -2, 
  orig_precision = 19, 
  not_complete = true
}

此函数目的:

  1. 追踪这个函数是从结果出发, 来分析case列相关的属性和数据信息, 倒推case列属性是如何执行的
  2. 对output_table[0].attrs[0][0]的数据做分析, 有以下奇怪的地方
    1. mode的值是Tianmu::common::ColOperation::LISTING, 这个是自然属性, 而并不是case这种计算的属性。这种属性会导致直接取值, 而非取值后的case计算
    2. buffer = 0x0 缓存列的中间结果, 其值为空, 也就是没有拿到数据, 结合mode是LISTING, 其值符合LISTING属性的预期
  3. 所以可以分析出, case属性的列, 在执行的时候, 是按照LISTING进行处理的, 而并没有做case运算

Query::AddColumnForMysqlExpression

调用堆栈:

#0  Tianmu::core::Query::AddColumnForMysqlExpression (this=0x7fd519f6e750, mysql_expression=0x7fd254024e70, tmp_table=..., 
    alias=0x7fd2540065c8 "case when  age IS NOT NULL THEN age else 33 end", oper=Tianmu::common::ColOperation::LISTING, distinct=false, group_by=false)
    at /root/work/stonedb-dev-20230605/storage/tianmu/core/query_compile.cpp:932
#1  0x0000000002d223ec in Tianmu::core::Query::AddFields (this=0x7fd519f6e750, fields=..., tmp_table=..., base_table=..., group_by_clause=false, num_of_added_fields=@0x7fd519f6e4c4: 0, 
    ignore_minmax=false, aggregation_used=@0x7fd519f6e40f: false) at /root/work/stonedb-dev-20230605/storage/tianmu/core/query_compile.cpp:507
#2  0x0000000002d25efd in Tianmu::core::Query::Compile (this=0x7fd519f6e750, compiled_query=0x7fd519f6e680, selects_list=0x7fd254004bc0, last_distinct=0x0, res_tab=0x0, ignore_limit=false, 
    left_expr_for_subselect=0x0, oper_for_subselect=0x0, ignore_minmax=false, for_subq_in_where=false) at /root/work/stonedb-dev-20230605/storage/tianmu/core/query_compile.cpp:1210
#3  0x0000000002ceb69c in Tianmu::core::Engine::Execute (this=0x7218d50, thd=0x7fd254000e10, lex=0x7fd254003138, result_output=0x7fd254006a68, unit_for_union=0x0)
    at /root/work/stonedb-dev-20230605/storage/tianmu/core/engine_execute.cpp:472
#4  0x0000000002cea825 in Tianmu::core::Engine::HandleSelect (this=0x7218d50, thd=0x7fd254000e10, lex=0x7fd254003138, result=@0x7fd519f6edc8: 0x7fd254006a68, setup_tables_done_option=0, 
    res=@0x7fd519f6edc4: 0, is_optimize_after_tianmu=@0x7fd519f6edbc: 1, tianmu_free_join=@0x7fd519f6edc0: 1, with_insert=0)
    at /root/work/stonedb-dev-20230605/storage/tianmu/core/engine_execute.cpp:243
#5  0x0000000003084b13 in Tianmu::DBHandler::ha_my_tianmu_query (thd=0x7fd254000e10, lex=0x7fd254003138, result_output=@0x7fd519f6edc8: 0x7fd254006a68, setup_tables_done_option=0, 
    res=@0x7fd519f6edc4: 0, is_optimize_after_tianmu=@0x7fd519f6edbc: 1, tianmu_free_join=@0x7fd519f6edc0: 1, with_insert=0)
    at /root/work/stonedb-dev-20230605/storage/tianmu/sql/ha_my_tianmu.cpp:95
#6  0x0000000002427aa8 in execute_sqlcom_select (thd=0x7fd254000e10, all_tables=0x7fd254014a88) at /root/work/stonedb-dev-20230605/sql/sql_parse.cc:5204
#7  0x0000000002420e1e in mysql_execute_command (thd=0x7fd254000e10, first_level=true) at /root/work/stonedb-dev-20230605/sql/sql_parse.cc:2847
#8  0x0000000002428b0d in mysql_parse (thd=0x7fd254000e10, parser_state=0x7fd519f6ff90) at /root/work/stonedb-dev-20230605/sql/sql_parse.cc:5642
#9  0x000000000241db04 in dispatch_command (thd=0x7fd254000e10, com_data=0x7fd519f70730, command=COM_QUERY) at /root/work/stonedb-dev-20230605/sql/sql_parse.cc:1495
#10 0x000000000241c945 in do_command (thd=0x7fd254000e10) at /root/work/stonedb-dev-20230605/sql/sql_parse.cc:1034
#11 0x000000000254eeb5 in handle_connection (arg=0x965db20) at /root/work/stonedb-dev-20230605/sql/conn_handler/connection_handler_per_thread.cc:313
#12 0x0000000002c1e6f4 in pfs_spawn_thread (arg=0x9490860) at /root/work/stonedb-dev-20230605/storage/perfschema/pfs.cc:2197
#13 0x00007fd569c2b1ca in start_thread () from /lib64/libpthread.so.0
#14 0x00007fd566aa1e73 in clone () from /lib64/libc.so.6

函数实现:

Query::AddFields

QueryRouteTo Query::AddFields(List<Item> &fields, TabID const &tmp_table, TabID const &base_table,
                              bool const group_by_clause, int &num_of_added_fields, bool ignore_minmax,
                              bool &aggregation_used) {
  List_iterator_fast<Item> li(fields);
  Item *item;
  int added = 0;
  item = li++;
  while (item) {
    WrapStatus ws;
    common::ColOperation oper;
    bool distinct;
    if (QueryRouteTo::kToMySQL == OperationUnmysterify(item, oper, distinct, group_by_clause))
      return QueryRouteTo::kToMySQL;

    if (IsAggregationItem(item))
      aggregation_used = true;

    // in case of transformed subquery sometimes we need to revert back
    // transformation to MIN/MAX
    if (ignore_minmax && (oper == common::ColOperation::MIN || oper == common::ColOperation::MAX))
      oper = common::ColOperation::LISTING;

    // select PHYSICAL COLUMN or AGGREGATION over PHYSICAL COLUMN
    if ((IsFieldItem(item) || IsAggregationOverFieldItem(item)) &&
        (IsLocalColumn(item, tmp_table) || (!base_table.IsNullID() && IsLocalColumn(item, base_table))))
      AddColumnForPhysColumn(item, tmp_table, base_table, oper, distinct, false, item->item_name.ptr());
    // REF to FIELD_ITEM
    else if (item->type() == Item::REF_ITEM) {
      item = UnRef(item);
      continue;
    }
    // if ((UnRef(item)->type() == Item_tianmufield::enumTIANMUFiledItem::TIANMUFIELD_ITEM ||
    //      UnRef(item)->type() == Item_tianmufield::FIELD_ITEM) &&
    //     IsLocalColumn(UnRef(item), tmp_table))
    //   AddColumnForPhysColumn(UnRef(item), tmp_table, oper, distinct, false, false);
    // else {
    //   //
    // }
    else if (IsAggregationItem(item) && (((Item_sum *)item)->get_arg(0))->type() == Item::REF_ITEM &&
             (UnRef(((Item_sum *)item)->get_arg(0))->type() == Item_tianmufield::get_tianmuitem_type() ||
              (UnRef(((Item_sum *)item)->get_arg(0))->type() == Item_tianmufield::FIELD_ITEM)) &&
             IsLocalColumn(UnRef(((Item_sum *)item)->get_arg(0)), tmp_table))
      // AGGR on REF to FIELD_ITEM
      AddColumnForPhysColumn(UnRef(((Item_sum *)item)->get_arg(0)), tmp_table, TabID(), oper, distinct, false,
                             item->item_name.ptr());
    else if (IsAggregationItem(item)) {
      // select AGGREGATION over EXPRESSION
      Item_sum *item_sum = (Item_sum *)item;
      if (item_sum->get_arg_count() > 1 || HasAggregation(item_sum->get_arg(0)))
        return QueryRouteTo::kToMySQL;
      if (IsCountStar(item_sum)) {  // count(*) doesn't need any virtual column
        AttrID at;
        cq->AddColumn(at, tmp_table, CQTerm(), oper, item_sum->item_name.ptr(), false);
        field_alias2num[TabIDColAlias(tmp_table.n, item_sum->item_name.ptr())] = at.n;
      } else {
        MysqlExpression *expr;
        ws = WrapMysqlExpression(item_sum->get_arg(0), tmp_table, expr, false, false);
        if (ws == WrapStatus::FAILURE)
          return QueryRouteTo::kToMySQL;
        AddColumnForMysqlExpression(expr, tmp_table,
                                    ignore_minmax ? item_sum->get_arg(0)->item_name.ptr() : item_sum->item_name.ptr(),
                                    oper, distinct);
      }
    } else if (item->type() == Item::SUBSELECT_ITEM) {
      CQTerm term;
      AttrID at;
      if (Item2CQTerm(item, term, tmp_table,
                      /*group_by_clause ? HAVING_FILTER :*/ CondType::WHERE_COND) == QueryRouteTo::kToMySQL)
        return QueryRouteTo::kToMySQL;
      cq->AddColumn(at, tmp_table, term, common::ColOperation::LISTING, item->item_name.ptr(), distinct);
      field_alias2num[TabIDColAlias(tmp_table.n, item->item_name.ptr())] = at.n;
    } else {
      // select EXPRESSION
      if (HasAggregation(item)) {
        oper = common::ColOperation::DELAYED;
        aggregation_used = true;
      }
      MysqlExpression *expr(nullptr);
      ws = WrapMysqlExpression(item, tmp_table, expr, false, oper == common::ColOperation::DELAYED);
      if (ws == WrapStatus::FAILURE)
        return QueryRouteTo::kToMySQL;
      if (!item->item_name.ptr()) {
        Item_func_conv_charset *item_conv = dynamic_cast<Item_func_conv_charset *>(item);
        if (item_conv) {
          Item **ifunc_args = item_conv->arguments();
          AddColumnForMysqlExpression(expr, tmp_table, ifunc_args[0]->item_name.ptr(), oper, distinct);
        } else {
          AddColumnForMysqlExpression(expr, tmp_table, item->item_name.ptr(), oper, distinct);
        }
      } else
        AddColumnForMysqlExpression(expr, tmp_table, item->item_name.ptr(), oper, distinct);
    }
    added++;
    item = li++;
  }
  num_of_added_fields = added;
  return QueryRouteTo::kToTianmu;
}

(gdb) p item
$30 = (Item_func_case *) 0x7fd254005840

此函数目的:

  1. case的列属性构建tianmu的查询序列, mode设置为LISTING导致了case运算的缺失, 那么就需要分析构建查询序列的列属性的过程
  2. 分析该函数有这么几个目的:
    1. 尝试将tianmu的列属性与mysql/sql的列属性保持一致, 补充确实的case运算
    2. 如果tianmu引擎无法计算该属性, 直接拿到item的信息进行处理

Tianmu::core::Query::AddColumnForMysqlExpression

调用堆栈:

#0  Tianmu::core::Query::AddColumnForMysqlExpression (this=0x7fb151126750, mysql_expression=0x7fae4401c470, tmp_table=..., 
    alias=0x7fae44010808 "case when  age IS NOT NULL THEN age else 33 end", oper=Tianmu::common::ColOperation::LISTING, distinct=false, group_by=false)
    at /root/work/stonedb-dev-20230605/storage/tianmu/core/query_compile.cpp:932
#1  0x0000000002d223ec in Tianmu::core::Query::AddFields (this=0x7fb151126750, fields=..., tmp_table=..., base_table=..., group_by_clause=false, num_of_added_fields=@0x7fb1511264c4: 0, 
    ignore_minmax=false, aggregation_used=@0x7fb15112640f: false) at /root/work/stonedb-dev-20230605/storage/tianmu/core/query_compile.cpp:507
#2  0x0000000002d25efd in Tianmu::core::Query::Compile (this=0x7fb151126750, compiled_query=0x7fb151126680, selects_list=0x7fae44005520, last_distinct=0x0, res_tab=0x0, ignore_limit=false, 
    left_expr_for_subselect=0x0, oper_for_subselect=0x0, ignore_minmax=false, for_subq_in_where=false) at /root/work/stonedb-dev-20230605/storage/tianmu/core/query_compile.cpp:1210
#3  0x0000000002ceb69c in Tianmu::core::Engine::Execute (this=0x79ded50, thd=0x7fae44000e10, lex=0x7fae44003138, result_output=0x7fae440120e8, unit_for_union=0x0)
    at /root/work/stonedb-dev-20230605/storage/tianmu/core/engine_execute.cpp:472
#4  0x0000000002cea825 in Tianmu::core::Engine::HandleSelect (this=0x79ded50, thd=0x7fae44000e10, lex=0x7fae44003138, result=@0x7fb151126dc8: 0x7fae440120e8, setup_tables_done_option=0, 
    res=@0x7fb151126dc4: 0, is_optimize_after_tianmu=@0x7fb151126dbc: 1, tianmu_free_join=@0x7fb151126dc0: 1, with_insert=0)
    at /root/work/stonedb-dev-20230605/storage/tianmu/core/engine_execute.cpp:243
#5  0x0000000003084b13 in Tianmu::DBHandler::ha_my_tianmu_query (thd=0x7fae44000e10, lex=0x7fae44003138, result_output=@0x7fb151126dc8: 0x7fae440120e8, setup_tables_done_option=0, 
    res=@0x7fb151126dc4: 0, is_optimize_after_tianmu=@0x7fb151126dbc: 1, tianmu_free_join=@0x7fb151126dc0: 1, with_insert=0)
    at /root/work/stonedb-dev-20230605/storage/tianmu/sql/ha_my_tianmu.cpp:95
#6  0x0000000002427aa8 in execute_sqlcom_select (thd=0x7fae44000e10, all_tables=0x7fae44013648) at /root/work/stonedb-dev-20230605/sql/sql_parse.cc:5204
#7  0x0000000002420e1e in mysql_execute_command (thd=0x7fae44000e10, first_level=true) at /root/work/stonedb-dev-20230605/sql/sql_parse.cc:2847
#8  0x0000000002428b0d in mysql_parse (thd=0x7fae44000e10, parser_state=0x7fb151127f90) at /root/work/stonedb-dev-20230605/sql/sql_parse.cc:5642
#9  0x000000000241db04 in dispatch_command (thd=0x7fae44000e10, com_data=0x7fb151128730, command=COM_QUERY) at /root/work/stonedb-dev-20230605/sql/sql_parse.cc:1495
#10 0x000000000241c945 in do_command (thd=0x7fae44000e10) at /root/work/stonedb-dev-20230605/sql/sql_parse.cc:1034
#11 0x000000000254eeb5 in handle_connection (arg=0x9e23f40) at /root/work/stonedb-dev-20230605/sql/conn_handler/connection_handler_per_thread.cc:313
#12 0x0000000002c1e6f4 in pfs_spawn_thread (arg=0x9c38930) at /root/work/stonedb-dev-20230605/storage/perfschema/pfs.cc:2197
#13 0x00007fb15b44a1ca in start_thread () from /lib64/libpthread.so.0
#14 0x00007fb1582c0e73 in clone () from /lib64/libc.so.6

函数实现:

vcolumn::VirtualColumn *Query::CreateColumnFromExpression(std::vector<MysqlExpression *> const &exprs,
                                                          TempTable *temp_table, int temp_table_alias,
                                                          MultiIndex *mind) {
  DEBUG_ASSERT(exprs.size() > 0);
  if (exprs.size() != 1) {
    DEBUG_ASSERT(0);
  }

  vcolumn::VirtualColumn *vc = nullptr;

  Item *item = exprs[0]->GetItem();
  if (exprs[0]->IsDeterministic() && (exprs[0]->GetVars().empty())) {
    ColumnType type(exprs[0]->EvalType());
    vc = new vcolumn::ConstColumn(*(exprs[0]->Evaluate()), type, true);
  } else if (vcolumn::VirtualColumn::IsConstExpression(exprs[0], temp_table_alias, &temp_table->GetAliases()) &&
             exprs[0]->IsDeterministic()) {
    if (IsFieldItem(item)) {
      // a special case when a naked column is a parameter
      // without this column type would be a seen by mysql, not TIANMU.
      // e.g. timestamp would be string 19
      TabID tab;
      AttrID col;
      tab.n = exprs[0]->GetVars().begin()->tab;
      col.n = exprs[0]->GetVars().begin()->col;
      col.n = col.n < 0 ? -col.n - 1 : col.n;
      ColumnType ct = ta[-tab.n - 1]->GetColumnType(col.n);
      vc = new vcolumn::ConstExpressionColumn(exprs[0], ct, temp_table, temp_table_alias, mind);
    } else
      vc = new vcolumn::ConstExpressionColumn(exprs[0], temp_table, temp_table_alias, mind);
  } else {
    if (tianmu_control_.isOn()) {
      if ((item->type()) == Item::FUNC_ITEM) {
        Item_func *ifunc = down_cast<Item_func *>(item);
        tianmu_control_.lock(mind->m_conn->GetThreadID())
            << "Unoptimized expression near '" << ifunc->func_name() << "'" << system::unlock;
      }
    }
    vc = new vcolumn::ExpressionColumn(exprs[0], temp_table, temp_table_alias, mind);
    if (static_cast<vcolumn::ExpressionColumn *>(vc)->GetStringType() == MysqlExpression::StringType::STRING_TIME &&
        vc->TypeName() != common::ColumnType::TIME) {  // common::CT::TIME is already as int64_t
      vcolumn::TypeCastColumn *tcc = new vcolumn::String2DateTimeCastColumn(vc, ColumnType(common::ColumnType::TIME));
      temp_table->AddVirtColumn(vc);
      vc = tcc;
    }
  }

  MysqlExpression::SetOfVars params = vc->GetParams();
  MysqlExpression::TypOfVars types;
  for (auto &iter : params) {
    types[iter] = ta[-iter.tab - 1]->GetColumnType(iter.col < 0 ? -iter.col - 1 : iter.col);
  }
  vc->SetParamTypes(&types);
  return vc;
}

此函数目的:

  1. 利用MysqlExpression中的信息,生成 VirtualColumn

TempTable::Materialize

调用堆栈:

#0  Tianmu::core::TempTable::Materialize (this=0x7fae44eaa1c0, in_subq=false, sender=0x796f3a0, lazy=false) at /root/work/stonedb-dev-20230605/storage/tianmu/core/temp_table.cpp:1955
#1  0x0000000002cebc6e in Tianmu::core::Engine::Execute (this=0x79ded50, thd=0x7fae44000e10, lex=0x7fae44003138, result_output=0x7fae44006a88, unit_for_union=0x0)
    at /root/work/stonedb-dev-20230605/storage/tianmu/core/engine_execute.cpp:518
#2  0x0000000002cea825 in Tianmu::core::Engine::HandleSelect (this=0x79ded50, thd=0x7fae44000e10, lex=0x7fae44003138, result=@0x7fb151126dc8: 0x7fae44006a88, setup_tables_done_option=0, 
    res=@0x7fb151126dc4: 0, is_optimize_after_tianmu=@0x7fb151126dbc: 1, tianmu_free_join=@0x7fb151126dc0: 1, with_insert=0)
    at /root/work/stonedb-dev-20230605/storage/tianmu/core/engine_execute.cpp:243
#3  0x0000000003084b13 in Tianmu::DBHandler::ha_my_tianmu_query (thd=0x7fae44000e10, lex=0x7fae44003138, result_output=@0x7fb151126dc8: 0x7fae44006a88, setup_tables_done_option=0, 
    res=@0x7fb151126dc4: 0, is_optimize_after_tianmu=@0x7fb151126dbc: 1, tianmu_free_join=@0x7fb151126dc0: 1, with_insert=0)
    at /root/work/stonedb-dev-20230605/storage/tianmu/sql/ha_my_tianmu.cpp:95
#4  0x0000000002427aa8 in execute_sqlcom_select (thd=0x7fae44000e10, all_tables=0x7fae44f39648) at /root/work/stonedb-dev-20230605/sql/sql_parse.cc:5204
#5  0x0000000002420e1e in mysql_execute_command (thd=0x7fae44000e10, first_level=true) at /root/work/stonedb-dev-20230605/sql/sql_parse.cc:2847
#6  0x0000000002428b0d in mysql_parse (thd=0x7fae44000e10, parser_state=0x7fb151127f90) at /root/work/stonedb-dev-20230605/sql/sql_parse.cc:5642
#7  0x000000000241db04 in dispatch_command (thd=0x7fae44000e10, com_data=0x7fb151128730, command=COM_QUERY) at /root/work/stonedb-dev-20230605/sql/sql_parse.cc:1495
#8  0x000000000241c945 in do_command (thd=0x7fae44000e10) at /root/work/stonedb-dev-20230605/sql/sql_parse.cc:1034
#9  0x000000000254eeb5 in handle_connection (arg=0x9e23f40) at /root/work/stonedb-dev-20230605/sql/conn_handler/connection_handler_per_thread.cc:313
#10 0x0000000002c1e6f4 in pfs_spawn_thread (arg=0x9c38930) at /root/work/stonedb-dev-20230605/storage/perfschema/pfs.cc:2197
#11 0x00007fb15b44a1ca in start_thread () from /lib64/libpthread.so.0
#12 0x00007fb1582c0e73 in clone () from /lib64/libc.so.6

函数实现:

void TempTable::Materialize(bool in_subq, ResultSender *sender, bool lazy) {
  MEASURE_FET("TempTable::Materialize()");
  if (sender)
    sender->SetAffectRows(no_obj);
  CreateDisplayableAttrP();
  CalculatePageSize();
  int64_t offset = 0;  // controls the first object to be materialized
  int64_t limit = -1;  // if(limit>=0)  ->  for(row = offset; row < offset +
                       // limit; row++) ....
  bool limits_present = mode.top;
  bool exists_only = mode.exists;

  if (limits_present) {
    offset = (mode.param1 >= 0 ? mode.param1 : 0);
    limit = (mode.param2 >= 0 ? mode.param2 : 0);
    // applied => reset to default values
    mode.top = false;
    mode.param2 = -1;
    mode.param1 = 0;
  }
  int64_t local_offset = 0;  // controls the first object to be materialized in a given algorithm
  int64_t local_limit = -1;
  if (materialized && (order_by.size() > 0 || limits_present) && no_obj) {
    // if TempTable is already materialized but some additional constraints were
    // specified, e.g., order by or limit this is typical case for union, where
    // constraints are specified for the result of union after materialization
    if (limits_present) {
      local_offset = offset;
      local_limit = std::min(limit, (int64_t)no_obj - offset);
      local_limit = local_limit < 0 ? 0 : local_limit;
    } else
      local_limit = no_obj;
    if (exists_only) {
      if (local_limit == 0)  // else no change needed
        no_obj = 0;
      return;
    }

    if (order_by.size() != 0 && no_obj > 1) {
      std::shared_ptr<TempTable> temporary_source_table =
          CreateMaterializedCopy(true, in_subq);  // true: translate definition of ordering
      OrderByAndMaterialize(order_by, local_limit, local_offset);
      DeleteMaterializedCopy(temporary_source_table);
    } else if (limits_present)
      ApplyOffset(local_limit, local_offset);
    order_by.clear();
    return;
  }

  if ((materialized && !this->lazy) || (this->lazy && no_obj == no_materialized)) {
    return;
  }

  bool table_distinct = this->mode.distinct;
  bool distinct_on_materialized = false;
  for (uint i = 0; i < NumOfAttrs(); i++)
    if (attrs[i]->mode != common::ColOperation::LISTING)
      group_by = true;
  if (table_distinct && group_by) {
    distinct_on_materialized = true;
    table_distinct = false;
  }

  if (order_by.size() > 0 || group_by || this->mode.distinct || force_full_materialize)
    lazy = false;
  this->lazy = lazy;

  bool no_rows_too_large = filter.mind_->TooManyTuples();
  no_obj = -1;         // no_obj not calculated yet - wait for better moment
  VerifyAttrsSizes();  // resize attr[i] buffers basing on the current
                       // multiindex state

  // the case when there is no grouping of attributes, check also DISTINCT
  // modifier of TT
  if (!group_by && !table_distinct) {
    DEBUG_ASSERT(!distinct_on_materialized);  // should by false here, otherwise must be
                                              // added to conditions below

    if (limits_present) {
      if (no_rows_too_large && order_by.size() == 0)
        no_obj = offset + limit;  // offset + limit in the worst case
      else
        no_obj = filter.mind_->NumOfTuples();
      if (no_obj <= offset) {
        no_obj = 0;
        materialized = true;
        order_by.clear();
        return;
      }
      local_offset = offset;
      local_limit = std::min(limit, (int64_t)no_obj - offset);
      local_limit = local_limit < 0 ? 0 : local_limit;
    } else {
      no_obj = filter.mind_->NumOfTuples();
      local_limit = no_obj;
    }
    if (exists_only) {
      order_by.clear();
      return;
    }
    output_mind.Clear();
    output_mind.AddDimension_cross(local_limit);  // an artificial dimension for result

    CalculatePageSize();  // recalculate, as no_obj might changed
    // perform order by: in this case it can be done on source tables, not on
    // the result
    bool materialized_by_ordering = false;
    if (CanOrderSources())
      // false if no sorting used
      materialized_by_ordering = this->OrderByAndMaterialize(order_by, local_limit, local_offset, sender);
    if (!materialized_by_ordering) {  // not materialized yet?
      // materialize without aggregations. If ordering then do not send result
      if (order_by.size() == 0)
        FillMaterializedBuffers(local_limit, local_offset, sender, lazy);
      else  // in case of order by we need to materialize all rows to be next
            // ordered
        FillMaterializedBuffers(no_obj, 0, nullptr, lazy);
    }
  } else {
    // GROUP BY or DISTINCT -  compute aggregations
    if (limits_present && !distinct_on_materialized && order_by.size() == 0) {
      local_offset = offset;
      local_limit = limit;
      if (exists_only)
        local_limit = 1;
    }
    if (HasHavingConditions() && in_subq)
      having_conds[0].tree->Simplify(true);

    ResultSender *local_sender = (distinct_on_materialized || order_by.size() > 0 ? nullptr : sender);
    AggregationAlgorithm aggr(this);
    aggr.Aggregate(table_distinct, local_limit, local_offset,
                   local_sender);  // this->tree (HAVING) used inside
    if (no_obj == 0) {
      order_by.clear();
      return;
    }

    output_mind.Clear();
    output_mind.AddDimension_cross(no_obj);  // an artificial dimension for result
  }

  local_offset = 0;
  local_limit = -1;

  // DISTINCT after grouping
  if (distinct_on_materialized && no_obj > 1) {
    if (limits_present && order_by.size() == 0) {
      local_offset = std::min((int64_t)no_obj, offset);
      local_limit = std::min(limit, (int64_t)no_obj - local_offset);
      local_limit = local_limit < 0 ? 0 : local_limit;
      if (no_obj <= offset) {
        no_obj = 0;
        materialized = true;
        order_by.clear();
        return;
      }
    } else
      local_limit = no_obj;
    if (exists_only)
      local_limit = 1;
    std::shared_ptr<TempTable> temporary_source_table = CreateMaterializedCopy(false, in_subq);
    ResultSender *local_sender = (order_by.size() > 0 ? nullptr : sender);

    AggregationAlgorithm aggr(this);
    aggr.Aggregate(true, local_limit, local_offset,
                   local_sender);  // true => select-level distinct
    DeleteMaterializedCopy(temporary_source_table);
    output_mind.Clear();
    output_mind.AddDimension_cross(no_obj);  // an artificial dimension for result
  }                                          // end of distinct part
  // ORDER BY, if not sorted until now
  if (order_by.size() != 0) {
    if (limits_present) {
      local_offset = std::min((int64_t)no_obj, offset);
      local_limit = std::min(limit, (int64_t)no_obj - local_offset);
      local_limit = local_limit < 0 ? 0 : local_limit;
      if (no_obj <= offset) {
        no_obj = 0;
        materialized = true;
        order_by.clear();
        return;
      }
    } else
      local_limit = no_obj;
    if (no_obj > 1 && !exists_only) {
      std::shared_ptr<TempTable> temporary_source_table =
          CreateMaterializedCopy(true, in_subq);  // true: translate definition of ordering
      OrderByAndMaterialize(order_by, local_limit, local_offset, sender);
      DeleteMaterializedCopy(temporary_source_table);
    }
    order_by.clear();
    output_mind.Clear();
    output_mind.AddDimension_cross(no_obj);  // an artificial dimension for result
  }
  materialized = true;
}

此函数执行完后的case列属性中的数据

(gdb) p result.attrs[0][0]
$5 = (Tianmu::core::TempTable::Attr) {
  <Tianmu::core::PhysicalColumn> = {
    <Tianmu::core::Column> = {
      ct = {
        type = Tianmu::common::ColumnType::BIGINT, 
        unsigned_flag_ = false, 
        precision = 19, 
        scale = 0, 
        internal_size = 8, 
        display_size = 20, 
        collation = {
          collation = 0x44664e0 <my_charset_bin>, 
          derivation = DERIVATION_NONE, 
          repertoire = 3
        }, 
        fmt = Tianmu::common::PackFmt::DEFAULT, 
        flag = std::bitset
      }
    }, 
    members of Tianmu::core::PhysicalColumn: 
    _vptr.PhysicalColumn = 0x44057d0 <vtable for Tianmu::core::TempTable::Attr+16>, 
    is_unique = false, 
    is_unique_updated = false
  }, 
  members of Tianmu::core::TempTable::Attr: 
  si = {
    separator = "", 
    order = st_order::ORDER_NOT_RELEVANT
  }, 
  buffer = 0x7fae44eaa480, 
  no_obj = 1, 
  no_power = 16, 
  no_materialized = 1, 
  page_size = 1, 
  alias = 0x7fae44016fd0 "case when  age IS NOT NULL THEN age else 33 end", 
  mode = Tianmu::common::ColOperation::LISTING, 
  distinct = false, 
  term = {
    type = Tianmu::common::ColumnType::UNK, 
    vc = 0x7fae44011e50, 
    cond_value = std::vector of length 0, capacity 0, 
    cond_numvalue = std::shared_ptr<Tianmu::utils::Hash64> (empty) = {
      get() = 0x0
    }, 
    vc_id = 0, 
    is_vc_owner = false, 
    item = 0x0
  }, 
--Type <RET> for more, q to quit, c to continue without paging--
  dim = -2, 
  orig_precision = 19, 
  not_complete = false
}

此函数目的:

  1. 执行完APPLY_CONS过滤了条件之后, 就开始执行该函数
  2. 函数内部的逻辑自然是对conds条件处理完成之后的临时表, 做处理, 包含聚合, 排序
  3. 对比正确的case里属性, 在此函数处理过后, 就获取到了正确的值!
  4. 具体的给case列属性赋值, 是在属性了聚合运算之后

AggregationAlgorithm::MultiDimensionalGroupByScan

调用堆栈:

#0  Tianmu::core::AggregationAlgorithm::MultiDimensionalGroupByScan (this=0x7fb1511264c0, gbw=..., limit=@0x7fb151126118: 1, offset=@0x7fb151126548: 0, sender=0x796f3a0, 
    limit_less_than_no_groups=false) at /root/work/stonedb-dev-20230605/storage/tianmu/optimizer/aggregation_algorithm.cpp:373
#1  0x0000000002fb80c3 in Tianmu::core::AggregationAlgorithm::Aggregate (this=0x7fb1511264c0, just_distinct=false, limit=@0x7fb151126540: -1, offset=@0x7fb151126548: 0, sender=0x796f3a0)
    at /root/work/stonedb-dev-20230605/storage/tianmu/optimizer/aggregation_algorithm.cpp:209
#2  0x0000000002d404d6 in Tianmu::core::TempTable::Materialize (this=0x7fae44eaa1c0, in_subq=false, sender=0x796f3a0, lazy=false)
    at /root/work/stonedb-dev-20230605/storage/tianmu/core/temp_table.cpp:2084
#3  0x0000000002cebc6e in Tianmu::core::Engine::Execute (this=0x79ded50, thd=0x7fae44000e10, lex=0x7fae44003138, result_output=0x7fae44006a88, unit_for_union=0x0)
    at /root/work/stonedb-dev-20230605/storage/tianmu/core/engine_execute.cpp:518
#4  0x0000000002cea825 in Tianmu::core::Engine::HandleSelect (this=0x79ded50, thd=0x7fae44000e10, lex=0x7fae44003138, result=@0x7fb151126dc8: 0x7fae44006a88, setup_tables_done_option=0, 
    res=@0x7fb151126dc4: 0, is_optimize_after_tianmu=@0x7fb151126dbc: 1, tianmu_free_join=@0x7fb151126dc0: 1, with_insert=0)
    at /root/work/stonedb-dev-20230605/storage/tianmu/core/engine_execute.cpp:243
#5  0x0000000003084b13 in Tianmu::DBHandler::ha_my_tianmu_query (thd=0x7fae44000e10, lex=0x7fae44003138, result_output=@0x7fb151126dc8: 0x7fae44006a88, setup_tables_done_option=0, 
    res=@0x7fb151126dc4: 0, is_optimize_after_tianmu=@0x7fb151126dbc: 1, tianmu_free_join=@0x7fb151126dc0: 1, with_insert=0)
    at /root/work/stonedb-dev-20230605/storage/tianmu/sql/ha_my_tianmu.cpp:95
#6  0x0000000002427aa8 in execute_sqlcom_select (thd=0x7fae44000e10, all_tables=0x7fae44f39648) at /root/work/stonedb-dev-20230605/sql/sql_parse.cc:5204
#7  0x0000000002420e1e in mysql_execute_command (thd=0x7fae44000e10, first_level=true) at /root/work/stonedb-dev-20230605/sql/sql_parse.cc:2847
#8  0x0000000002428b0d in mysql_parse (thd=0x7fae44000e10, parser_state=0x7fb151127f90) at /root/work/stonedb-dev-20230605/sql/sql_parse.cc:5642
#9  0x000000000241db04 in dispatch_command (thd=0x7fae44000e10, com_data=0x7fb151128730, command=COM_QUERY) at /root/work/stonedb-dev-20230605/sql/sql_parse.cc:1495
#10 0x000000000241c945 in do_command (thd=0x7fae44000e10) at /root/work/stonedb-dev-20230605/sql/sql_parse.cc:1034
#11 0x000000000254eeb5 in handle_connection (arg=0x9e23f40) at /root/work/stonedb-dev-20230605/sql/conn_handler/connection_handler_per_thread.cc:313
#12 0x0000000002c1e6f4 in pfs_spawn_thread (arg=0x9c38930) at /root/work/stonedb-dev-20230605/storage/perfschema/pfs.cc:2197
#13 0x00007fb15b44a1ca in start_thread () from /lib64/libpthread.so.0
#14 0x00007fb1582c0e73 in clone () from /lib64/libc.so.6

对比当派生中有值且聚合能非NULL的结果时的数据:

此函数目的:

  1. 不但执行聚合运算, 并且还要将结果temp_table和sender
  2. 之所以在执行聚合时还要将结果填充, 最根本的原因是tianmu的执行并非是mysql/sql层的火山模型, 每一个操作符做清晰的划分, 数据自下而上的流动。而是使用类似monetdb的物化模型, 尽可能将中间结果进行物化
  3. 第373行表明了为什么当派生表为空时, case列属性为NULL, 而不进行case计算
    1. 对其他列的数据的填充, 是以聚合运算结果的大小为依据
    2. 当聚合运算为空时, 对其他列不做赋值, 导致也不执行case运算

分析该查询出错的问题总结:

  1. 在定位查询出错的时候,大部分的情况是对相关代码逻辑并不熟悉, 需要根据线索逐步的定位出问题
  2. 能否定位并解决问题, 是相对于设计一个流程,更为复杂的过程, 因为这个过程并不是正向的,也就是说需要根据现有结果, 来反推当前代码的设计意图,并找到对于当前场景未考虑到的因素

tianmu引擎对于case列属性以及聚合处理的设计意图:

  1. 将设计意图的分析放在最后, 是因为设计这个东西最容易引起误解,大部分人都是照本宣科的某种教条来编写代码, 而忽视了代码或者说解决方案所要解决的问题
  2. tianmu引擎的查询执行非常明显的区别于mysql/sql层的查询执行, 对于操作符的处理尤其不同,这中间有一些数据库模型上的差异
    1. mysql/sql和innodb的配合是以行存为基础, 以表的访问为最基本的单元。而tianmu是以列为单位, 最小的处理单元是列, 更上层的所谓表的概念是不同的列的组合, 由此抽象出相当复杂的维度表的概念。
    2. myql/sql层的查询优化和查询执行采取了经典的火山模型, 操作符或者说算子相对独立
    3. tianmu采取了monetdb的思想, 采用了物化模型, 在执行时操作符之间的处理相对物化因素来说不是那么清晰, 例如在聚合处理的时候, 聚合的列与其他列的影响,以及将聚合后的结果或者说中间结果进行物化处理, 比较明显的就是对于temp_table的处理。

修复该查询错误的问题:
 

  1. 限于篇幅, 随后会对如何修复该场景下的查询问题, 如果重新设计相关逻辑和代码架构, 做更为详细的处理
  2. 这里需要指出, 只有当为了解决问题时, 所谓的代码设计和更宏观的架构才有意义, 而非某种死板的教条。代码架构必定是为了不但要解决当前的问题更是为了避免更为长远的场景下所出现的问题。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/623657.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【OpenCV DNN】Flask 视频监控目标检测教程 07

欢迎关注『OpenCV DNN Youcans』系列&#xff0c;持续更新中 【OpenCV DNN】Flask 视频监控目标检测教程 07 3.7 OpenCVFlask实时监控人脸识别cvFlask08 项目的文件树cvFlask08 项目的Python程序cvFlask08 项目的网页模板cvFlask08 项目的运行 本系列从零开始&#xff0c;详细…

2023上海国际嵌入式展 | 如何通过人工智能驱动的自动化测试工具提升嵌入式开发效率

2023年6月14日到16日&#xff0c;龙智将在2023上海国际嵌入式展&#xff08;embedded world China 2023&#xff09;A055展位亮相。同时&#xff0c;6月14日下午3:00-3:30&#xff0c;龙智资深DevSecOps顾问巫晓光将于创新技术及应用发展论坛第二论坛区&#xff08;A325展位&am…

idea代码检查插件

1&#xff0c;SonarLint Idea 安装 Sonar 插件提升代码质量_idea sonar插件_打代码的苏比特的博客-CSDN博客 2&#xff0c;immomo Mybatis XML Mapper SQL注入漏洞发现与一键修复-----项目静态代码安全审计idea插件工具MOMO CODE SEC INSPECTOR-Java_idea sql注入 插件_North…

【蓝桥刷题】备战国赛——交通信号

蓝桥杯2022国赛真题——交通信号 &#x1f680; 每日一题&#xff0c;冲刺国赛 &#x1f680; 题目导航&#xff1a; 交通信号 &#x1f387;思路&#xff1a; d i j k s t r a dijkstra dijkstra &#x1f531;思路分析&#xff1a; 要求从一点到另一点的最短距离&#xff0…

软件测试的生命周期、Bug

一、软件测试的生命周期 1、软件的生命周期&#xff1a; 需求分析&#xff1a;分析需求是否正确、完整。 设计&#xff1a;项目的上线时间、开始开发时间、测试时间、人员... 计划&#xff1a;设计技术文档、进行UI设计... 编码&#xff1a;写代码&#xff08;实现用户需求&am…

Tomcat启动闪退的详细解决方法(捕获的野生的java1.8.0_321和野生的Tomcat8实验)

1.实验说明 本实验将采用捕获的野生的java1.8.0_321和野生的Tomcat8进行实验。而且不需要安装服务。 2.配置声明&#xff1a; java -version javac -version CATALINA_HOME 说明&#xff1a;CATALINA_HOME配置到放置到tomcat的目录 Path 说明&#xff1a;Path路径配置到tomca…

如和使用matlab实现香农编码和解码

文章目录 前言效果截图如下代码解析完整代码完结 撒花 前言 在网上看了好多 , 都是对香农进行编码的案例 , 却没有 进行解码的操作 , 今天就来补齐这个欠缺 效果截图如下 代码解析 text 你好; % 待编码的文本定义一个字符串类型的变量text&#xff0c;其值为’你好’。 [en…

2023水博会新热门:北斗时空智能 助力水利数字孪生

当“北斗”遇上“智慧水利”将会碰撞出怎样的新意&#xff1f; 6月7日&#xff0c;2023中国水博会暨第十八届中国&#xff08;国际&#xff09;水务高峰论坛正式召开。会上&#xff0c;由千寻位置提出的“北斗时空智能助力水利数字孪生”理念及相应解决方案&#xff0c;受到了与…

肠道菌群、性激素与疾病:探索它们的交互作用

谷禾健康 我们的身体中有很多不同的器官&#xff0c;组织&#xff0c;腺体等会产生许多信号分子来精确控制和影响身体的反应和活动&#xff0c;这些信号分子包括激素、神经递质、生长因子、细胞因子等。它们可以促进或抑制细胞的生长和分化&#xff0c;调节细胞间的相互作用和通…

搭建lanproxy客户端与服务端实现内网穿透

一、首先要配置java环境 1.可以使用这个&#xff0c;或者官网下载&#xff0c;或者其他版本皆可。https://download.csdn.net/download/qq_44821149/87878658 2.采用jdk-8u144-linux-x64.zip压缩包。java version 为1.8.0_144。 3.具体操作为&#xff1a; mkdir /usr/java u…

使用 Iptables 命令详细图文教程

目录 一、防火墙管理工具 二、Iptables 2.1 策略与规则链 2.2 基本的命令参数 2.2.1. 在 iptables 命令后添加 -L 参数查看已有的防火墙规则链。 2.2.2 在 iptables 命令后添加 -F 参数清空已有的防火墙规则链。 2.2.3 把 INPUT 规则链的默认策略设置为拒绝。 2.2.4…

【Web服务应用】部署LVS-DR集群

LVS-DR集群 一、LVS-DR工作原理二 、DR数据包流向分析2.1DR模式中名词解释2.2数据包流向 三、ARP问题3.1问题一&#xff1a;IP 地址冲突3.2 问题二&#xff1a;第二次再有访问请求 四、LVS-DR实战 一、LVS-DR工作原理 LVS-DR&#xff08;Linux Virtual Server Director Server&…

Pandas的groupby用法说明

Pandas的groupby用法说明 1、功能说明 按官方文档说明groupby功能&#xff0c;可以参考与SQL中的分组操作进行理解。 By “group by” we are referring to a process involving one or more of the following steps: Splitting the data into groups based on some criteri…

VMware® vSphere虚拟化平台限制虚拟机网卡速率一例

本文介绍VMware vSphere对虚拟服务器进行网卡限速的案例 一、案例背景 目前有一套生产环境的虚拟化平台基于VMware vSphere构建。宿主机外连网卡均为主、备各10Gb&#xff0c;核心交换机出口至外部网络带宽也是10Gb。某业务系统使用nginx搭建了两台固件升级服务器对外提供下载…

Spark RDD的创建

文章目录 一、RDD为何物&#xff08;一&#xff09;RDD概念&#xff08;二&#xff09;RDD示例&#xff08;三&#xff09;RDD主要特征 二、做好准备工作&#xff08;一&#xff09;准备文件1、准备本地系统文件2、启动HDFS服务3、上传文件到HDFS &#xff08;二&#xff09;启…

免费升级到 iOS 17 Developer Beta:官方Apple Store升级方案与爱思助手方法比较

&#x1f337;&#x1f341; 博主 libin9iOak带您 Go to New World.✨&#x1f341; &#x1f984; 个人主页——libin9iOak的博客&#x1f390; &#x1f433; 《面试题大全》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33…

【unity】几种常用的拖拽方法(内置方法 + 接口 + Event Trigger组件)

前言 在Unity中实现拖拽的方法有多种&#xff0c;以下是几种常见的方法和它们的优缺点 1. 鼠标按键的点击事件 Input.GetMouseButtonDown和Input.GetMouseButtonUp 方法可以监测用户鼠标按键的点击事件&#xff0c;通过检测鼠标按钮的状态来实现拖拽效果。用户通过鼠标进行拖…

DolphinScheduler3.1.4 集群部署

文章目录 DolphinScheduler3.1.4 集群部署一、解压 dolphinScheduler3.1.4 安装包二、复制 mysql8.0.16 的驱动三、创建mysql数据库用户和权限四、配置 dolphinscheduler_env.sh 文件五、初始化元数据六、配置 install_env.sh 文件七、安装 dolphinScheduler DolphinScheduler3…

2核4G服务器阿里云4M和腾讯云5M性能价格对比

阿里云轻量应用服务器2核4G4M带宽297.98元12个月&#xff0c;腾讯云轻量2核4G5M服务器168元一年&#xff0c;628元3年&#xff0c;2核4G轻量应用服务器阿里云和腾讯云怎么选择&#xff1f;哪个性能比较好&#xff1f;阿腾云分享轻量应用服务器2核4G配置阿里云和腾讯云CPU、带宽…

插件分享 | Headshot ⼀击即中,对指定URL进行漏洞批量扫描

前言&#xff1a;在⼀次真实的攻防场景中&#xff0c;我们发现了⼀个存在 Struts2 漏洞的地址&#xff0c;这个地址在我们通过 Fuzz 获得的⼆级⽬录下&#xff0c;这使得 Goby 的爬⾍没有办法爬取到这⼀个⻚⾯&#xff0c;但是我们通过其它 Struts2 专扫⼯具检测发现这个地址确…