Skip to content

Commit

Permalink
MySQL Bypass Project Initial Change [non-RocksDB part]
Browse files Browse the repository at this point in the history
Summary:
MySQL bypass infrastructure change

1. Adds a new hton override `handlerton::handle_single_table_select` to allow hijacking of SELECT statement execution from any storage engine.
2. Add lex parsing for bypass hints in optimizer hint style /*+ bypass */ and /*+ no_bypass */
3. MyRocks overrides handle_single_table_select but does nothing and simply fallback.

This is a port from from bypass feature branch - change #1 of 3. I'm planning to port the changes in the following order:

1. MySQL lexer + SELECT hijacking (this one).
2. SELECT Statement parsing and checking
3. Execution of SELECT statement in bypass

Reference Patch: facebook/mysql-5.6@2d19dc0

-----
Porting Notes:

* The entry points have changed due to MySQL 8.0 refactoring to SELECT path, and now it is in `Sql_cmd_dml::execute_inner`. I'll also see if overriding the entire execute would be better but this should be good enough for now.
* The check for whether we should go to bypass handler is mostly the same, except st_select_lex_t is now SELECT_LEX.
* The lex parsing for hints was mostly ported as-is. I was also debating whether I should just use optimizer hints instead of hacking the lexer, but decided against using optimizer hint for now so that migration from 5.6 will be easier. We can migrate to optimizer hint once 8.0 migration is complete.

Differential Revision: D22807040

---------------------------------------------------------------------------------------------

[bypass] MySQL bypass change #2 - SELECT parsing [non-RocksDB part]

Summary:
This is change #2 that ports the SELECT statement parsing from feature-myrocks-bypass branch, including:

1. select_parser class that parses SELECT statements and validates the scenarios are supported, and error with proper message if not supported
2. You can control bypass policy with rocksdb_select_bypass_policy variable = 0 (ALWAYS OFF) / 1 (ALWAYS ON) / 2 (OPT IN) / 3 (OPT OUT).
3. A few metrics for number of SELECT bypass statements executed/rejected (due to not supported) /failed (something wrong in execution)

At this point this only dumps the SELECT statement parsed results without doing anything useful. The next change will be porting the query plan execution and point/range query execution part.

Please ignore the unused attributes for now - they'll be removed in next change once the execution part is ported.

Reference Patch:

facebook/mysql-5.6@e79b20a
facebook/mysql-5.6@8072e3d

 ---
Porting Notes:

There are a lot of small changes in this area but nothing too bad:
* Some bypass scenarios no longer fail or fail with different error message because of the new parse tree -> AST conversion process. For example, `SELECT *` now works correctly
* st_select_lex_t -> SELECT_LEX, and many members has changed name, such as where -> where_cond()
* protocol usage has changed to start_row, store, end_row in 8.0, which is much better.
* thd->query() is now a LEX_STRING
* SELECT option check (such as SELECT_DISTINCT) now uses active_options() and only check for SELECT_DISTINCT as there seems to be default value being set most likely due to side effect of having more parse tree processing being done. I checked the flags and it looks like only SELECT_DISTINCT are interesting and needs to be blocked.
* SELECT INTO OUTFILE check is changed to use results->needs_file_priviledge
* True/False const are now represented as Item_func_true/Item_func_false. For now I'm over-generalizing to functions that are const, and expect val_int/etc would do the right thing

Reviewed By: luqun

Differential Revision: D22808305

-----------------------------------------------------------------------------------------------

Introduce base select parser class

Summary:
To make it easier to add a new bypass rpc parser into existing bypass implementation (nosql_access.cc),
and make minimum change to bypass engine while adding a bypass rpc parser, I introduced a new base class,
base_select_parser. It includes all essential members and methods for parsing bypass sq/rpc queries.
Existing bypass sql parser (select_parser) and a new bypass rpc parser (not included in this diff)
inherits the base function.

Bypass RPC reference diff: D30493654

Reviewed By: yizhang82

Differential Revision: D31870628
  • Loading branch information
yizhang82 authored and inikep committed Apr 10, 2024
1 parent c41a5fd commit 30b3ef7
Show file tree
Hide file tree
Showing 5 changed files with 117 additions and 2 deletions.
6 changes: 6 additions & 0 deletions sql/handler.h
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,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
Expand Down Expand Up @@ -2324,6 +2326,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
Expand Down Expand Up @@ -2695,6 +2700,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};
Expand Down
4 changes: 3 additions & 1 deletion sql/item.h
Original file line number Diff line number Diff line change
Expand Up @@ -4169,8 +4169,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);
Expand Down
60 changes: 59 additions & 1 deletion sql/sql_lex.cc
Original file line number Diff line number Diff line change
Expand Up @@ -860,6 +860,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;
Expand All @@ -876,6 +896,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);
Expand Down Expand Up @@ -2151,7 +2208,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),
Expand Down
15 changes: 15 additions & 0 deletions sql/sql_lex.h
Original file line number Diff line number Diff line change
Expand Up @@ -1242,6 +1242,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(); }
Expand Down
34 changes: 34 additions & 0 deletions sql/sql_select.cc
Original file line number Diff line number Diff line change
Expand Up @@ -992,6 +992,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));
}

/**
Execute a DML statement.
This is the default implementation for a DML statement and uses a
Expand All @@ -1004,6 +1033,11 @@ bool optimize_secondary_engine(THD *thd) {
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;
Expand Down

0 comments on commit 30b3ef7

Please sign in to comment.