• Igor Babaev's avatar
    MDEV-23886 Reusing CTE inside a function fails with table doesn't exist · 04de6517
    Igor Babaev authored
    In the code existed just before this patch binding of a table reference to
    the specification of the corresponding CTE happens in the function
    open_and_process_table(). If the table reference is not the first in the
    query the specification is cloned in the same way as the specification of
    a view is cloned for any reference of the view. This works fine for
    standalone queries, but does not work for stored procedures / functions
    for the following reason.
    When the first call of a stored procedure/ function SP is processed the
    body of SP is parsed. When a query of SP is parsed the info on each
    encountered table reference is put into a TABLE_LIST object linked into
    a global chain associated with the query. When parsing of the query is
    finished the basic info on the table references from this chain except
    table references to derived tables and information schema tables is put
    in one hash table associated with SP. When parsing of the body of SP is
    finished this hash table is used to construct TABLE_LIST objects for all
    table references mentioned in SP and link them into the list of such
    objects passed to a pre-locking process that calls open_and_process_table()
    for each table from the list.
    When a TABLE_LIST for a view is encountered the view is opened and its
    specification is parsed. For any table reference occurred in
    the specification a new TABLE_LIST object is created to be included into
    the list for pre-locking. After all objects in the pre-locking have been
    looked through the tables mentioned in the list are locked. Note that the
    objects referenced CTEs are just skipped here as it is impossible to
    resolve these references without any info on the context where they occur.
    Now the statements from the body of SP are executed one by one that.
    At the very beginning of the execution of a query the tables used in the
    query are opened and open_and_process_table() now is called for each table
    reference mentioned in the list of TABLE_LIST objects associated with the
    query that was built when the query was parsed.
    For each table reference first the reference is checked against CTEs
    definitions in whose scope it occurred. If such definition is found the
    reference is considered resolved and if this is not the first reference
    to the found CTE the the specification of the CTE is re-parsed and the
    result of the parsing is added to the parsing tree of the query as a
    sub-tree. If this sub-tree contains table references to other tables they
    are added to the list of TABLE_LIST objects associated with the query in
    order the referenced tables to be opened. When the procedure that opens
    the tables comes to the TABLE_LIST object created for a non-first
    reference to a CTE it discovers that the referenced table instance is not
    locked and reports an error.
    Thus processing non-first table references to a CTE similar to how
    references to view are processed does not work for queries used in stored
    procedures / functions. And the main problem is that the current
    pre-locking mechanism employed for stored procedures / functions does not
    allow to save the context in which a CTE reference occur. It's not trivial
    to save the info about the context where a CTE reference occurs while the
    resolution of the table reference cannot be done without this context and
    consequentially the specification for the table reference cannot be
    determined.
    
    This patch solves the above problem by moving resolution of all CTE
    references at the parsing stage. More exactly references to CTEs occurred in
    a query are resolved right after parsing of the query has finished. After
    resolution any CTE reference it is marked as a reference to to derived
    table. So it is excluded from the hash table created for pre-locking used
    base tables and view when the first call of a stored procedure / function
    is processed.
    This solution required recursive calls of the parser. The function
    THD::sql_parser() has been added specifically for recursive invocations of
    the parser.
    04de6517
sql_yacc.yy 552 KB