First of all, before I get to comments inline, I'd like to say...great work so far! I'm really looking forward to the development of this plugin. :) The below are more of a stream-of-thought comments, not particularly well-structured, but hopefully get some thoughts circling.

Toru Maesaka wrote:
G'day!

As some of you already know, lately I've been experimenting on
making the current parser pluggable. I've managed to write a demo
that passes the test suite so I wanted to get some feedbacks on
what you all think.

The tree is at:
  http://bazaar.launchpad.net/~tmaesaka/drizzle/pluggable-parser

So, what I've done in this tree is simple. I replaced the current entry
point of the parser (mysql_parse) with a plugin entry point and pushed
out what was behind mysql_parse() into a mandatory module.

The idea is that a module implements a parser that populates the session
object using the provided query string, query length and a variable for the
discovered semicolon with this interface:


bool (*sql_parse)(Session *session, const char *query,
                         const size_t query_len, const char **found_semicolon);

OK, I'll start off with my "big picture" spiel, and then move on to a more realistic approach... :)

I would actually prefer that the interface for parsing not get wrapped up in the Session object. This is not be possible right away (explained below), but I would prefer to see a pointer to a more lightweight and parser-specific object passed to a parser plugin. For instance, a Node in an abstract syntax tree.

The idea here is that a plugin should not be passed any more information than it needs to do its job. Clean interfaces ensure that the boundaries of information are clearly defined and that objects only access those things about which they "know".

A parser knows the following:

* The input type it expects (typically a string of characters)
* A set of tokens (actually this is more the domain of the lexer, but bear with me...)
* A set of rules -- the grammar of the language
* The production is should output -- the "parse tree"

What it does *not* need to know about (or care about) is the myriad other things which are attached to the Session object:

* Status variables
* Configuration variables
* Diagnostics arenas
* Lock information
* Protocol information for communicating with the client socket
* Security contexts (ACL, RBAC)
* Replicator information
* Transaction information
* Much more, as you know...

By passing a pointer to the Session object, which is, unfortunately, designed in a very open, member-public, way, we risk the parser getting entangled in the ABI for the Session, which is an object bigger than the parser needs to care about.

That said, our existing parser unfortunately has the Session object thingsentangled in itself...which is obvious as soon as you open up the Yacc file and see things like this all over the place:

  Session *session= YYSession;
  session->lex->sql_command= SQLCOM_EMPTY_QUERY;

The macros at the top of the Yacc file are telling:

%{
/* session is passed as an argument to yyparse(), and subsequently
** to yylex().
** The type will be void*, so it must be  cast to (Session*) when used.
** Use the YYSession macro for this.
*/
#define YYPARSE_PARAM yysession
#define YYLEX_PARAM yysession
#define YYSession ((Session *)yysession)
...
#define Lex (YYSession->lex)
#define Select Lex->current_select

There are a number of functions that the session object is passed to from within the parser:

push_warning_printf()
select_lex->add_table_to_list()
ha_resolve_by_name()
add_field_to_list()
select_lex->alloc_index_hints()
session->add_item_to_list()
negate_expression()
handle_2003_note184_exception()
create_func_cast()
select_lex->nest_last_join()
push_new_name_resolution_context()
select_lex->init_nested_join()
select_lex->end_nested_join()
prepare_schema_table()

In addition, the session object's mem_root is supplied as an allocator to most of the Item constructors.

So, all of the above functions would need to be altered to not need the Session object (all of them can be.) and we'd want to pass in the mem_root pointer to the parse function instead of the Session object.

I'm thinking a goal for the *interface* would be something like the following for a C plugin:

bool (*sql_parse)
(ParseTree* node, MEM_ROOT *alloc, const char *query, const size_t query_len);

and this for a C++ plugin:

class SqlParseEvent
{
  public:
    bool operator()(ParseTree *node, MEM_ROOT *root, std::string query);
}

The basic idea being that the parser should focus on *parsing* and nothing else. Just parse the query into a tree and pass it back to the caller. That's it. No need to figure out how to send warnings to a client. No need for the parser to try and figure out whether the session has access to an object or not. Just build the parse tree and pass it back. Let other functions validate the tree. Parsers should parse. That's it. :)

After looking hard today at the current parser (which is, BTW, smaller than MySQL 4.1's parser and less than a third of the size of MySQL 6.0's) I strongly believe we can reduce the parser by at least another third (not the grammar, but the productions in the grammar rules) by removing the Session from the parser and reducing the amount of validation and checking the parser is doing and pulling that stuff out into separate functions...


Saying that, things weren't as simple as I wanted it to be and I had to do
the following to modularize the parser:

- Separate mysql_execute_command() from the parser for a "clean"
  separation of the parser from the core. I've added a wrapper function
  within the core called sql_parse_and_execute() to replace where
  previously parsing and query execution occurred at the same place.

Also, although my original intention was to only introduce one plugin
function, I had to unfortunately add another one called:


sql_parse_vcol_expr(Session *session, const char *vcol_expr,
                               const size_t length);


this is due to the way unpack_vcol_info_from_frm() in table.cc relies
on a direct call to the parser to translate the virtual column expression
from a .frm file into an Item object. So, I needed a way to directly call
the raw parser without going through housekeeping.

I reaaaaaally don't want this function but to kill this thing, we need to
come up with a way to create a virtual column object without relying
on the parser. Either that or I think we can kill this function when
Drizzle doesn't need .frm files anymore. I think this is currently being
worked on?

It will be going away shortly, since the frm is almost completely gone.

Anyhow, that's all I have to say for now! It would be great if you guys
could tell me whether I screwed up miserably or if its worth considering :)

I have lots more to talk about, but I'll leave it there for now...just to get the ideas flowing.

Cheers!

Jay


_______________________________________________
Mailing list: https://launchpad.net/~drizzle-discuss
Post to     : [email protected]
Unsubscribe : https://launchpad.net/~drizzle-discuss
More help   : https://help.launchpad.net/ListHelp

Reply via email to