diff --git a/sql/handler.h b/sql/handler.h index e14249f21b22..a1e0d1066e12 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -170,6 +170,8 @@ extern const char *binlog_format_names[]; extern TYPELIB tx_isolation_typelib; extern ulong total_ha_2pc; +class Query_block; + // the following is for checking tables #define HA_ADMIN_ALREADY_DONE 1 @@ -2339,6 +2341,9 @@ typedef bool (*check_fk_column_compat_t)( typedef bool (*is_reserved_db_name_t)(handlerton *hton, const char *name); +/* Overriding single table SELECT implementation if returns TRUE */ +typedef bool (*handle_single_table_select_t)(THD *thd, Query_block *select_lex); + /** Prepare the secondary engine for executing a statement. This function is called right after the secondary engine TABLE objects have been opened by @@ -2794,6 +2799,7 @@ struct handlerton { dict_get_server_version_t dict_get_server_version; dict_set_server_version_t dict_set_server_version; is_reserved_db_name_t is_reserved_db_name; + handle_single_table_select_t handle_single_table_select; /** Global handler flags. */ uint32 flags{0}; diff --git a/sql/item.h b/sql/item.h index a1525df06c5d..43a2cef2542c 100644 --- a/sql/item.h +++ b/sql/item.h @@ -4340,8 +4340,10 @@ class Item_equal; class Item_field : public Item_ident { typedef Item_ident super; - protected: + public: void set_field(Field *field); + + protected: void fix_after_pullout(Query_block *parent_query_block, Query_block *removed_query_block) override { super::fix_after_pullout(parent_query_block, removed_query_block); diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 88f29c2c2dbc..ae1a366e6a21 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -859,6 +859,26 @@ Yacc_state::~Yacc_state() { } } +const uchar to_upper_lex[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, + 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, + 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, + 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, + 90, 91, 92, 93, 94, 95, 96, 65, 66, 67, 68, 69, 70, 71, 72, + 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, + 88, 89, 90, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, + 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, + 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, + 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, + 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, + 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, + 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 192, + 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, + 208, 209, 210, 211, 212, 213, 214, 247, 216, 217, 218, 219, 220, 221, 222, + 255}; + static bool consume_optimizer_hints(Lex_input_stream *lip) { const my_lex_states *state_map = lip->query_charset->state_maps->main_map; int whitespace = 0; @@ -875,6 +895,43 @@ static bool consume_optimizer_hints(Lex_input_stream *lip) { lip->yylineno += newlines; lip->yySkipn(whitespace); // skip whitespace + /* + lookahead for bypass hint - this is safe as query buffer format + ensures null terminating character at the end as well as additional + data towards the end + */ + if (lip->yyPeekn(3) == ' ') { + if (to_upper_lex[lip->yyPeekn(4)] == 'B' && + to_upper_lex[lip->yyPeekn(5)] == 'Y' && + to_upper_lex[lip->yyPeekn(6)] == 'P' && + to_upper_lex[lip->yyPeekn(7)] == 'A' && + to_upper_lex[lip->yyPeekn(8)] == 'S' && + to_upper_lex[lip->yyPeekn(9)] == 'S' && lip->yyPeekn(10) == ' ' && + lip->yyPeekn(11) == '*' && lip->yyPeekn(12) == '/') { + /* HINT: turn on select bypass */ + lip->m_thd->lex->query_block->select_bypass_hint = + Query_block::SELECT_BYPASS_HINT_ON; + lip->yySkipn(13); + return false; + } else if (to_upper_lex[lip->yyPeekn(4)] == 'N' && + to_upper_lex[lip->yyPeekn(5)] == 'O' && + lip->yyPeekn(6) == '_' && + to_upper_lex[lip->yyPeekn(7)] == 'B' && + to_upper_lex[lip->yyPeekn(8)] == 'Y' && + to_upper_lex[lip->yyPeekn(9)] == 'P' && + to_upper_lex[lip->yyPeekn(10)] == 'A' && + to_upper_lex[lip->yyPeekn(11)] == 'S' && + to_upper_lex[lip->yyPeekn(12)] == 'S' && + lip->yyPeekn(13) == ' ' && lip->yyPeekn(14) == '*' && + lip->yyPeekn(15) == '/') { + /* HINT: turn off select bypass */ + lip->m_thd->lex->query_block->select_bypass_hint = + Query_block::SELECT_BYPASS_HINT_OFF; + lip->yySkipn(16); + return false; + } + } + Hint_scanner hint_scanner(lip->m_thd, lip->yylineno, lip->get_ptr(), lip->get_end_of_query() - lip->get_ptr(), lip->m_digest); @@ -2255,7 +2312,8 @@ Query_expression::Query_expression(enum_parsing_context parsing_context) */ Query_block::Query_block(MEM_ROOT *mem_root, Item *where, Item *having) - : fields(mem_root), + : select_bypass_hint(SELECT_BYPASS_HINT_DEFAULT), + fields(mem_root), ftfunc_list(&ftfunc_list_alloc), sj_nests(mem_root), first_context(&context), diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 3ac1f953b8c0..11e766504e38 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -1251,6 +1251,21 @@ class Query_block : public Query_term { bool remove_aggregates(THD *thd, Query_block *select); + /* + Status of bypass hint bypass and no_bypass. + It is up to the storage engine to interpret the meaning in the + hton::handle_single_table_select callback. Typically storage engine + can use this as hint to go talk to their own SELECT query engine and + bypass MySQL logic partially or completely. + */ + enum select_bypass_hint_type { + SELECT_BYPASS_HINT_DEFAULT, /* Neither bypass / no_bypass specified */ + SELECT_BYPASS_HINT_ON, /* bypass specified */ + SELECT_BYPASS_HINT_OFF, /* no_bypass specified */ + }; + + enum select_bypass_hint_type select_bypass_hint; + Query_expression *master_query_expression() const { return master; } Query_expression *first_inner_query_expression() const { return slave; } Query_block *outer_query_block() const { return master->outer_query_block(); } diff --git a/sql/sql_select.cc b/sql/sql_select.cc index fd78cd1ec130..ab71e73313d5 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -970,6 +970,35 @@ bool optimize_secondary_engine(THD *thd) { secondary_engine->optimize_secondary_engine(thd, thd->lex); } +/* Call out to handler to handle this select command */ +bool ha_handle_single_table_select(THD *thd, Query_expression *unit) { + /* Simple non-UNION non-NESTED query only */ + if (!unit->is_simple()) { + return false; + } + + Query_block *select_lex = unit->first_query_block(); + + /* Single table query only */ + if (select_lex->m_table_list.elements != 1) { + return false; + } + + Table_ref *table_list = select_lex->m_table_list.first; + if (!table_list) { + return false; + } + + TABLE *table = table_list->table; + if (!table) { + return false; + } + + handlerton *hton = table->s->db_type(); + return (hton && hton->handle_single_table_select && + hton->handle_single_table_select(thd, select_lex)); +} + void notify_plugins_after_select(THD *thd, const Sql_cmd *cmd) { auto executed_in = (cmd != nullptr && cmd->using_secondary_storage_engine()) ? SelectExecutedIn::kSecondaryEngine @@ -999,6 +1028,11 @@ void notify_plugins_after_select(THD *thd, const Sql_cmd *cmd) { bool Sql_cmd_dml::execute_inner(THD *thd) { Query_expression *unit = lex->unit; + if (ha_handle_single_table_select(thd, unit)) { + // We've handled the query + return thd->is_error(); + } + if (unit->optimize(thd, /*materialize_destination=*/nullptr, /*create_iterators=*/true, /*finalize_access_paths=*/true)) return true;