MDEV-25202: JSON_TABLE: Early table reference leads to unexpected result set
Address review input: switch Name_resolution_context::ignored_tables from table_map to a list of TABLE_LIST objects. The rationale is that table bits may be changed due to query rewrites, etc, which may potentially require updating ignored_tables.
This commit is contained in:
parent
0984b8ed08
commit
b0817ff8de
@ -790,6 +790,23 @@ ON t1.a = tt.b;
|
|||||||
a b c o
|
a b c o
|
||||||
1 1 {} 1
|
1 1 {} 1
|
||||||
2 2 [] NULL
|
2 2 [] NULL
|
||||||
|
prepare s from
|
||||||
|
"SELECT *
|
||||||
|
FROM
|
||||||
|
t1 RIGHT JOIN
|
||||||
|
v2 AS tt
|
||||||
|
LEFT JOIN
|
||||||
|
JSON_TABLE(CONCAT(tt.c,''), '$' COLUMNS(o FOR ORDINALITY)) AS jt
|
||||||
|
ON tt.b = jt.o
|
||||||
|
ON t1.a = tt.b";
|
||||||
|
execute s;
|
||||||
|
a b c o
|
||||||
|
1 1 {} 1
|
||||||
|
2 2 [] NULL
|
||||||
|
execute s;
|
||||||
|
a b c o
|
||||||
|
1 1 {} 1
|
||||||
|
2 2 [] NULL
|
||||||
DROP VIEW v2;
|
DROP VIEW v2;
|
||||||
DROP TABLE t1, t2;
|
DROP TABLE t1, t2;
|
||||||
#
|
#
|
||||||
|
@ -682,6 +682,19 @@ FROM
|
|||||||
ON tt.b = jt.o
|
ON tt.b = jt.o
|
||||||
ON t1.a = tt.b;
|
ON t1.a = tt.b;
|
||||||
|
|
||||||
|
prepare s from
|
||||||
|
"SELECT *
|
||||||
|
FROM
|
||||||
|
t1 RIGHT JOIN
|
||||||
|
v2 AS tt
|
||||||
|
LEFT JOIN
|
||||||
|
JSON_TABLE(CONCAT(tt.c,''), '$' COLUMNS(o FOR ORDINALITY)) AS jt
|
||||||
|
ON tt.b = jt.o
|
||||||
|
ON t1.a = tt.b";
|
||||||
|
execute s;
|
||||||
|
execute s;
|
||||||
|
|
||||||
|
|
||||||
DROP VIEW v2;
|
DROP VIEW v2;
|
||||||
DROP TABLE t1, t2;
|
DROP TABLE t1, t2;
|
||||||
|
|
||||||
|
15
sql/item.cc
15
sql/item.cc
@ -10713,3 +10713,18 @@ bool Item::cleanup_excluding_immutables_processor (void *arg)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool ignored_list_includes_table(ignored_tables_list_t list, TABLE_LIST *tbl)
|
||||||
|
{
|
||||||
|
if (!list)
|
||||||
|
return false;
|
||||||
|
List_iterator<TABLE_LIST> it(*list);
|
||||||
|
TABLE_LIST *list_tbl;
|
||||||
|
while ((list_tbl = it++))
|
||||||
|
{
|
||||||
|
if (list_tbl == tbl)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
@ -162,6 +162,9 @@ void dummy_error_processor(THD *thd, void *data);
|
|||||||
|
|
||||||
void view_error_processor(THD *thd, void *data);
|
void view_error_processor(THD *thd, void *data);
|
||||||
|
|
||||||
|
typedef List<TABLE_LIST>* ignored_tables_list_t;
|
||||||
|
bool ignored_list_includes_table(ignored_tables_list_t list, TABLE_LIST *tbl);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Instances of Name_resolution_context store the information necessary for
|
Instances of Name_resolution_context store the information necessary for
|
||||||
name resolution of Items and other context analysis of a query made in
|
name resolution of Items and other context analysis of a query made in
|
||||||
@ -236,7 +239,7 @@ struct Name_resolution_context: Sql_alloc
|
|||||||
Bitmap of tables that should be ignored when doing name resolution.
|
Bitmap of tables that should be ignored when doing name resolution.
|
||||||
Normally it is {0}. Non-zero values are used by table functions.
|
Normally it is {0}. Non-zero values are used by table functions.
|
||||||
*/
|
*/
|
||||||
table_map ignored_tables;
|
ignored_tables_list_t ignored_tables;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Security context of this name resolution context. It's used for views
|
Security context of this name resolution context. It's used for views
|
||||||
@ -247,7 +250,7 @@ struct Name_resolution_context: Sql_alloc
|
|||||||
Name_resolution_context()
|
Name_resolution_context()
|
||||||
:outer_context(0), table_list(0), select_lex(0),
|
:outer_context(0), table_list(0), select_lex(0),
|
||||||
error_processor_data(0),
|
error_processor_data(0),
|
||||||
ignored_tables(0),
|
ignored_tables(NULL),
|
||||||
security_ctx(0)
|
security_ctx(0)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
@ -44,9 +44,14 @@ static table_function_handlerton table_function_hton;
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
@brief
|
@brief
|
||||||
Collect a bitmap of tables that a given table function cannot have
|
Collect a set of tables that a given table function cannot have
|
||||||
references to.
|
references to.
|
||||||
|
|
||||||
|
@param
|
||||||
|
table_func The table function we are connecting info for
|
||||||
|
join_list The nested join to be processed
|
||||||
|
disallowed_tables Collect the tables here.
|
||||||
|
|
||||||
@detail
|
@detail
|
||||||
According to the SQL standard, a table function can refer to any table
|
According to the SQL standard, a table function can refer to any table
|
||||||
that's "preceding" it in the FROM clause.
|
that's "preceding" it in the FROM clause.
|
||||||
@ -80,16 +85,16 @@ static table_function_handlerton table_function_hton;
|
|||||||
we are ok with operating on the tables "in the left join order".
|
we are ok with operating on the tables "in the left join order".
|
||||||
|
|
||||||
@return
|
@return
|
||||||
TRUE - enumeration has found the Table Function instance. The bitmap is
|
0 - Continue
|
||||||
ready.
|
1 - Finish the process, success
|
||||||
FALSE - Otherwise
|
-1 - Finish the process, failure
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static
|
static
|
||||||
bool get_disallowed_table_deps_for_list(table_map table_func_bit,
|
int get_disallowed_table_deps_for_list(MEM_ROOT *mem_root,
|
||||||
List<TABLE_LIST> *join_list,
|
TABLE_LIST *table_func,
|
||||||
table_map *disallowed_tables)
|
List<TABLE_LIST> *join_list,
|
||||||
|
List<TABLE_LIST> *disallowed_tables)
|
||||||
{
|
{
|
||||||
TABLE_LIST *table;
|
TABLE_LIST *table;
|
||||||
NESTED_JOIN *nested_join;
|
NESTED_JOIN *nested_join;
|
||||||
@ -99,23 +104,25 @@ bool get_disallowed_table_deps_for_list(table_map table_func_bit,
|
|||||||
{
|
{
|
||||||
if ((nested_join= table->nested_join))
|
if ((nested_join= table->nested_join))
|
||||||
{
|
{
|
||||||
if (get_disallowed_table_deps_for_list(table_func_bit,
|
int res;
|
||||||
&nested_join->join_list,
|
if ((res= get_disallowed_table_deps_for_list(mem_root, table_func,
|
||||||
disallowed_tables))
|
&nested_join->join_list,
|
||||||
return true;
|
disallowed_tables)))
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
*disallowed_tables |= table->table->map;
|
if (disallowed_tables->push_back(table, mem_root))
|
||||||
if (table_func_bit == table->table->map)
|
return -1;
|
||||||
|
if (table == table_func)
|
||||||
{
|
{
|
||||||
// This is the JSON_TABLE(...) that are we're computing dependencies
|
// This is the JSON_TABLE(...) that are we're computing dependencies
|
||||||
// for.
|
// for.
|
||||||
return true;
|
return 1; // Finish the processing
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return 0; // Continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -129,19 +136,31 @@ bool get_disallowed_table_deps_for_list(table_map table_func_bit,
|
|||||||
See get_disallowed_table_deps_for_list
|
See get_disallowed_table_deps_for_list
|
||||||
|
|
||||||
@return
|
@return
|
||||||
Bitmap of tables that table function can NOT have references to.
|
NULL - Out of memory
|
||||||
|
Other - A list of tables that the function cannot have references to. May
|
||||||
|
be empty.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static
|
static
|
||||||
table_map get_disallowed_table_deps(JOIN *join, table_map table_func_bit)
|
List<TABLE_LIST>* get_disallowed_table_deps(MEM_ROOT *mem_root,
|
||||||
|
SELECT_LEX *select,
|
||||||
|
TABLE_LIST *table_func)
|
||||||
{
|
{
|
||||||
table_map disallowed_tables= 0;
|
List<TABLE_LIST> *disallowed_tables;
|
||||||
if (!get_disallowed_table_deps_for_list(table_func_bit, join->join_list,
|
|
||||||
&disallowed_tables))
|
if (!(disallowed_tables = new List<TABLE_LIST>))
|
||||||
{
|
return NULL;
|
||||||
// We haven't found the table with table_func_bit in all tables?
|
|
||||||
DBUG_ASSERT(0);
|
int res= get_disallowed_table_deps_for_list(mem_root, table_func,
|
||||||
}
|
select->join_list,
|
||||||
|
disallowed_tables);
|
||||||
|
|
||||||
|
// The collection process must have finished
|
||||||
|
DBUG_ASSERT(res != 0);
|
||||||
|
|
||||||
|
if (res == -1)
|
||||||
|
return NULL; // Out of memory
|
||||||
|
|
||||||
return disallowed_tables;
|
return disallowed_tables;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1034,49 +1053,57 @@ bool push_table_function_arg_context(LEX *lex, MEM_ROOT *alloc)
|
|||||||
Perform name-resolution phase tasks
|
Perform name-resolution phase tasks
|
||||||
|
|
||||||
@detail
|
@detail
|
||||||
- The only argument that needs resolution is the JSON text
|
The only argument that needs name resolution is the first parameter which
|
||||||
- Then, we need to set dependencies: if JSON_TABLE refers to table's
|
has the JSON text:
|
||||||
column, e.g.
|
|
||||||
|
|
||||||
JSON_TABLE (t1.col ... ) AS t2
|
JSON_TABLE(json_doc, ... )
|
||||||
|
|
||||||
then it can be computed only after table t1.
|
The argument may refer to other tables and uses special name resolution
|
||||||
- The dependencies must not form a loop.
|
rules (see get_disallowed_table_deps_for_list for details). This function
|
||||||
|
sets up Name_resolution_context object appropriately before calling
|
||||||
|
fix_fields for the argument.
|
||||||
|
|
||||||
|
@return
|
||||||
|
false OK
|
||||||
|
true Fatal error
|
||||||
*/
|
*/
|
||||||
|
|
||||||
int Table_function_json_table::setup(THD *thd, TABLE_LIST *sql_table,
|
bool Table_function_json_table::setup(THD *thd, TABLE_LIST *sql_table,
|
||||||
SELECT_LEX *s_lex)
|
SELECT_LEX *s_lex)
|
||||||
{
|
{
|
||||||
TABLE *t= sql_table->table;
|
|
||||||
|
|
||||||
thd->where= "JSON_TABLE argument";
|
thd->where= "JSON_TABLE argument";
|
||||||
{
|
|
||||||
bool save_is_item_list_lookup;
|
|
||||||
bool res;
|
|
||||||
save_is_item_list_lookup= s_lex->is_item_list_lookup;
|
|
||||||
s_lex->is_item_list_lookup= 0;
|
|
||||||
|
|
||||||
|
if (!m_context_setup_done)
|
||||||
|
{
|
||||||
|
m_context_setup_done= true;
|
||||||
// Prepare the name resolution context. First, copy the context that is
|
// Prepare the name resolution context. First, copy the context that is
|
||||||
// used for name resolution of the WHERE clause
|
// used for name resolution of the WHERE clause
|
||||||
*m_context= s_lex->context;
|
*m_context= s_lex->context;
|
||||||
|
|
||||||
// Then, restrict it to only allow to refer to tables that come before the
|
// Then, restrict it to only allow to refer to tables that come before the
|
||||||
// table function reference
|
// table function reference
|
||||||
m_context->ignored_tables= get_disallowed_table_deps(s_lex->join, t->map);
|
if (!(m_context->ignored_tables=
|
||||||
|
get_disallowed_table_deps(thd->stmt_arena->mem_root, s_lex,
|
||||||
// Do the same what setup_without_group() does: do not count the referred
|
sql_table)))
|
||||||
// fields in non_agg_field_used:
|
return TRUE; // Error
|
||||||
const bool saved_non_agg_field_used= s_lex->non_agg_field_used();
|
|
||||||
|
|
||||||
res= m_json->fix_fields_if_needed(thd, &m_json);
|
|
||||||
|
|
||||||
s_lex->is_item_list_lookup= save_is_item_list_lookup;
|
|
||||||
s_lex->set_non_agg_field_used(saved_non_agg_field_used);
|
|
||||||
|
|
||||||
if (res)
|
|
||||||
return TRUE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool save_is_item_list_lookup;
|
||||||
|
save_is_item_list_lookup= s_lex->is_item_list_lookup;
|
||||||
|
s_lex->is_item_list_lookup= 0;
|
||||||
|
|
||||||
|
// Do the same what setup_without_group() does: do not count the referred
|
||||||
|
// fields in non_agg_field_used:
|
||||||
|
const bool saved_non_agg_field_used= s_lex->non_agg_field_used();
|
||||||
|
|
||||||
|
bool res= m_json->fix_fields_if_needed(thd, &m_json);
|
||||||
|
|
||||||
|
s_lex->is_item_list_lookup= save_is_item_list_lookup;
|
||||||
|
s_lex->set_non_agg_field_used(saved_non_agg_field_used);
|
||||||
|
|
||||||
|
if (res)
|
||||||
|
return TRUE; // Error
|
||||||
|
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -198,7 +198,7 @@ public:
|
|||||||
List<Json_table_column> m_columns;
|
List<Json_table_column> m_columns;
|
||||||
|
|
||||||
/*** Name resolution functions ***/
|
/*** Name resolution functions ***/
|
||||||
int setup(THD *thd, TABLE_LIST *sql_table, SELECT_LEX *s_lex);
|
bool setup(THD *thd, TABLE_LIST *sql_table, SELECT_LEX *s_lex);
|
||||||
|
|
||||||
int walk_items(Item_processor processor, bool walk_subquery,
|
int walk_items(Item_processor processor, bool walk_subquery,
|
||||||
void *argument);
|
void *argument);
|
||||||
@ -226,7 +226,8 @@ public:
|
|||||||
|
|
||||||
/*** Construction interface to be used from the parser ***/
|
/*** Construction interface to be used from the parser ***/
|
||||||
Table_function_json_table(Item *json):
|
Table_function_json_table(Item *json):
|
||||||
m_json(json)
|
m_json(json),
|
||||||
|
m_context_setup_done(false)
|
||||||
{
|
{
|
||||||
cur_parent= &m_nested_path;
|
cur_parent= &m_nested_path;
|
||||||
last_sibling_hook= &m_nested_path.m_nested;
|
last_sibling_hook= &m_nested_path.m_nested;
|
||||||
@ -250,6 +251,8 @@ private:
|
|||||||
/* Context to be used for resolving the first argument. */
|
/* Context to be used for resolving the first argument. */
|
||||||
Name_resolution_context *m_context;
|
Name_resolution_context *m_context;
|
||||||
|
|
||||||
|
bool m_context_setup_done;
|
||||||
|
|
||||||
/* Current NESTED PATH level being parsed */
|
/* Current NESTED PATH level being parsed */
|
||||||
Json_table_nested_path *cur_parent;
|
Json_table_nested_path *cur_parent;
|
||||||
|
|
||||||
|
@ -6101,7 +6101,8 @@ Field *
|
|||||||
find_field_in_table_ref(THD *thd, TABLE_LIST *table_list,
|
find_field_in_table_ref(THD *thd, TABLE_LIST *table_list,
|
||||||
const char *name, size_t length,
|
const char *name, size_t length,
|
||||||
const char *item_name, const char *db_name,
|
const char *item_name, const char *db_name,
|
||||||
const char *table_name, table_map ignored_tables,
|
const char *table_name,
|
||||||
|
ignored_tables_list_t ignored_tables,
|
||||||
Item **ref,
|
Item **ref,
|
||||||
bool check_privileges, bool allow_rowid,
|
bool check_privileges, bool allow_rowid,
|
||||||
uint *cached_field_index_ptr,
|
uint *cached_field_index_ptr,
|
||||||
@ -6194,8 +6195,13 @@ find_field_in_table_ref(THD *thd, TABLE_LIST *table_list,
|
|||||||
TABLE_LIST *table;
|
TABLE_LIST *table;
|
||||||
while ((table= it++))
|
while ((table= it++))
|
||||||
{
|
{
|
||||||
if (table->table && (table->table->map & ignored_tables))
|
/*
|
||||||
|
Check if the table is in the ignore list. Only base tables can be in
|
||||||
|
the ignore list.
|
||||||
|
*/
|
||||||
|
if (table->table && ignored_list_includes_table(ignored_tables, table))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if ((fld= find_field_in_table_ref(thd, table, name, length, item_name,
|
if ((fld= find_field_in_table_ref(thd, table, name, length, item_name,
|
||||||
db_name, table_name, ignored_tables,
|
db_name, table_name, ignored_tables,
|
||||||
ref, check_privileges, allow_rowid,
|
ref, check_privileges, allow_rowid,
|
||||||
@ -6322,8 +6328,8 @@ Field *find_field_in_table_sef(TABLE *table, const char *name)
|
|||||||
first_table list of tables to be searched for item
|
first_table list of tables to be searched for item
|
||||||
last_table end of the list of tables to search for item. If NULL
|
last_table end of the list of tables to search for item. If NULL
|
||||||
then search to the end of the list 'first_table'.
|
then search to the end of the list 'first_table'.
|
||||||
ignored_tables Bitmap of tables that should be ignored. Do not try
|
ignored_tables Set of tables that should be ignored. Do not try to
|
||||||
to find the field in those.
|
find the field in those.
|
||||||
ref if 'item' is resolved to a view field, ref is set to
|
ref if 'item' is resolved to a view field, ref is set to
|
||||||
point to the found view field
|
point to the found view field
|
||||||
report_error Degree of error reporting:
|
report_error Degree of error reporting:
|
||||||
@ -6351,7 +6357,7 @@ Field *find_field_in_table_sef(TABLE *table, const char *name)
|
|||||||
Field *
|
Field *
|
||||||
find_field_in_tables(THD *thd, Item_ident *item,
|
find_field_in_tables(THD *thd, Item_ident *item,
|
||||||
TABLE_LIST *first_table, TABLE_LIST *last_table,
|
TABLE_LIST *first_table, TABLE_LIST *last_table,
|
||||||
table_map ignored_tables,
|
ignored_tables_list_t ignored_tables,
|
||||||
Item **ref, find_item_error_report_type report_error,
|
Item **ref, find_item_error_report_type report_error,
|
||||||
bool check_privileges, bool register_tree_change)
|
bool check_privileges, bool register_tree_change)
|
||||||
{
|
{
|
||||||
@ -6475,7 +6481,8 @@ find_field_in_tables(THD *thd, Item_ident *item,
|
|||||||
for (; cur_table != last_table ;
|
for (; cur_table != last_table ;
|
||||||
cur_table= cur_table->next_name_resolution_table)
|
cur_table= cur_table->next_name_resolution_table)
|
||||||
{
|
{
|
||||||
if (cur_table->table && (cur_table->table->map & ignored_tables))
|
if (cur_table->table &&
|
||||||
|
ignored_list_includes_table(ignored_tables, cur_table))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
Field *cur_field= find_field_in_table_ref(thd, cur_table, name, length,
|
Field *cur_field= find_field_in_table_ref(thd, cur_table, name, length,
|
||||||
|
@ -195,14 +195,15 @@ bool fill_record(THD *thd, TABLE *table, Field **field, List<Item> &values,
|
|||||||
Field *
|
Field *
|
||||||
find_field_in_tables(THD *thd, Item_ident *item,
|
find_field_in_tables(THD *thd, Item_ident *item,
|
||||||
TABLE_LIST *first_table, TABLE_LIST *last_table,
|
TABLE_LIST *first_table, TABLE_LIST *last_table,
|
||||||
table_map ignored_tables,
|
ignored_tables_list_t ignored_tables,
|
||||||
Item **ref, find_item_error_report_type report_error,
|
Item **ref, find_item_error_report_type report_error,
|
||||||
bool check_privileges, bool register_tree_change);
|
bool check_privileges, bool register_tree_change);
|
||||||
Field *
|
Field *
|
||||||
find_field_in_table_ref(THD *thd, TABLE_LIST *table_list,
|
find_field_in_table_ref(THD *thd, TABLE_LIST *table_list,
|
||||||
const char *name, size_t length,
|
const char *name, size_t length,
|
||||||
const char *item_name, const char *db_name,
|
const char *item_name, const char *db_name,
|
||||||
const char *table_name, table_map ignored_tables,
|
const char *table_name,
|
||||||
|
ignored_tables_list_t ignored_tables,
|
||||||
Item **ref, bool check_privileges, bool allow_rowid,
|
Item **ref, bool check_privileges, bool allow_rowid,
|
||||||
uint *cached_field_index_ptr,
|
uint *cached_field_index_ptr,
|
||||||
bool register_tree_change, TABLE_LIST **actual_table);
|
bool register_tree_change, TABLE_LIST **actual_table);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user