MXS-1340 Report true table and not alias name

With this change, for a statement like

    SELECT t2.a FROM t1 t2;

the affected field is reported as t1.a and not as t2.a, as it
was before.

For a statement like

    SELECT t.f FROM d.t;

qc_mysqlembedded will now return "d.t.f" as the affected field,
while qc_sqlite will still return "t.f" as both implementations did
before. In qc_mysqlembedded's case that is a side-effect of the
alias handling. To get qc_sqlite to return the same (which would
be good), the table names would have to be collected in a smarter
way than they are now.
This commit is contained in:
Johan Wikman 2017-08-07 13:22:24 +03:00
parent b9588a89ac
commit 4f4151bca9
5 changed files with 99 additions and 24 deletions

View File

@ -2180,6 +2180,7 @@ static bool should_exclude(const char* name, List<Item>* excludep)
}
static void add_field_info(parsing_info_t* info,
st_select_lex* select,
const char* database,
const char* table,
const char* column,
@ -2190,6 +2191,35 @@ static void add_field_info(parsing_info_t* info,
QC_FIELD_INFO item = { (char*)database, (char*)table, (char*)column, usage };
if (!database && table)
{
st_select_lex* s = select;
while ((item.table == table) && s)
{
TABLE_LIST* tbl = s->table_list.first;
while ((item.table == table) && tbl)
{
if (tbl->alias &&
(strcmp(tbl->alias, table) == 0) &&
(strcmp(tbl->table_name, "*") != 0))
{
// The dummy default database "skygw_virtual" is not included.
if (tbl->db && *tbl->db && (strcmp(tbl->db, "skygw_virtual") != 0))
{
item.database = (char*)tbl->db;
}
item.table = (char*)tbl->table_name;
}
tbl = tbl->next_local;
}
s = s->outer_select();
}
}
size_t i;
for (i = 0; i < info->field_infos_len; ++i)
{
@ -2326,7 +2356,11 @@ static void add_function_info(parsing_info_t* info,
}
}
static void add_field_info(parsing_info_t* pi, Item_field* item, uint32_t usage, List<Item>* excludep)
static void add_field_info(parsing_info_t* pi,
st_select_lex* select,
Item_field* item,
uint32_t usage,
List<Item>* excludep)
{
const char* database = item->db_name;
const char* table = item->table_name;
@ -2417,10 +2451,14 @@ static void add_field_info(parsing_info_t* pi, Item_field* item, uint32_t usage,
break;
}
add_field_info(pi, database, table, column, usage, excludep);
add_field_info(pi, select, database, table, column, usage, excludep);
}
static void add_field_info(parsing_info_t* pi, Item* item, uint32_t usage, List<Item>* excludep)
static void add_field_info(parsing_info_t* pi,
st_select_lex* select,
Item* item,
uint32_t usage,
List<Item>* excludep)
{
const char* database = NULL;
const char* table = NULL;
@ -2431,7 +2469,7 @@ static void add_field_info(parsing_info_t* pi, Item* item, uint32_t usage, List<
strncpy(column, s, l);
column[l] = 0;
add_field_info(pi, database, table, column, usage, excludep);
add_field_info(pi, select, database, table, column, usage, excludep);
}
typedef enum collect_source
@ -2504,6 +2542,7 @@ static bool should_function_be_ignored(parsing_info_t* pi, const char* func_name
}
static void update_field_infos(parsing_info_t* pi,
st_select_lex* select,
collect_source_t source,
Item* item,
uint32_t usage,
@ -2518,13 +2557,13 @@ static void update_field_infos(parsing_info_t* pi,
while (Item *i = ilist++)
{
update_field_infos(pi, source, i, usage, excludep);
update_field_infos(pi, select, source, i, usage, excludep);
}
}
break;
case Item::FIELD_ITEM:
add_field_info(pi, static_cast<Item_field*>(item), usage, excludep);
add_field_info(pi, select, static_cast<Item_field*>(item), usage, excludep);
break;
case Item::REF_ITEM:
@ -2533,7 +2572,7 @@ static void update_field_infos(parsing_info_t* pi,
{
Item_ref* ref_item = static_cast<Item_ref*>(item);
add_field_info(pi, item, usage, excludep);
add_field_info(pi, select, item, usage, excludep);
size_t n_items = ref_item->cols();
@ -2543,7 +2582,7 @@ static void update_field_infos(parsing_info_t* pi,
if (reffed_item != ref_item)
{
update_field_infos(pi, source, ref_item->element_index(i), usage, excludep);
update_field_infos(pi, select, source, ref_item->element_index(i), usage, excludep);
}
}
}
@ -2557,7 +2596,7 @@ static void update_field_infos(parsing_info_t* pi,
for (size_t i = 0; i < n_items; ++i)
{
update_field_infos(pi, source, row_item->element_index(i), usage, excludep);
update_field_infos(pi, select, source, row_item->element_index(i), usage, excludep);
}
}
break;
@ -2665,7 +2704,7 @@ static void update_field_infos(parsing_info_t* pi,
for (size_t i = 0; i < n_items; ++i)
{
update_field_infos(pi, source, items[i], usage, excludep);
update_field_infos(pi, select, source, items[i], usage, excludep);
}
}
break;
@ -2692,7 +2731,7 @@ static void update_field_infos(parsing_info_t* pi,
)
if (in_subselect_item->left_expr_orig)
{
update_field_infos(pi, source,
update_field_infos(pi, select, source, // TODO: Might be wrong select.
in_subselect_item->left_expr_orig, usage, excludep);
}
st_select_lex* ssl = in_subselect_item->get_select_lex();
@ -2789,7 +2828,7 @@ static void update_field_infos(parsing_info_t* pi,
while (Item *item = ilist++)
{
update_field_infos(pi, COLLECT_SELECT, item, usage, NULL);
update_field_infos(pi, select, COLLECT_SELECT, item, usage, NULL);
}
if (select->group_list.first)
@ -2799,7 +2838,7 @@ static void update_field_infos(parsing_info_t* pi,
{
Item* item = *order->item;
update_field_infos(pi, COLLECT_GROUP_BY, item, QC_USED_IN_GROUP_BY,
update_field_infos(pi, select, COLLECT_GROUP_BY, item, QC_USED_IN_GROUP_BY,
&select->item_list);
order = order->next;
@ -2808,7 +2847,7 @@ static void update_field_infos(parsing_info_t* pi,
if (select->where)
{
update_field_infos(pi, COLLECT_WHERE,
update_field_infos(pi, select, COLLECT_WHERE,
select->where,
QC_USED_IN_WHERE,
&select->item_list);
@ -2927,7 +2966,7 @@ int32_t qc_mysql_get_field_info(GWBUF* buf, const QC_FIELD_INFO** infos, uint32_
List_iterator<Item> ilist(lex->value_list);
while (Item* item = ilist++)
{
update_field_infos(pi, COLLECT_SELECT, item, 0, NULL);
update_field_infos(pi, lex->current_select, COLLECT_SELECT, item, 0, NULL);
}
if ((lex->sql_command == SQLCOM_INSERT) ||
@ -2938,7 +2977,7 @@ int32_t qc_mysql_get_field_info(GWBUF* buf, const QC_FIELD_INFO** infos, uint32_
List_iterator<Item> ilist(lex->field_list);
while (Item *item = ilist++)
{
update_field_infos(pi, COLLECT_SELECT, item, 0, NULL);
update_field_infos(pi, lex->current_select, COLLECT_SELECT, item, 0, NULL);
}
if (lex->insert_list)
@ -2946,7 +2985,7 @@ int32_t qc_mysql_get_field_info(GWBUF* buf, const QC_FIELD_INFO** infos, uint32_
List_iterator<Item> ilist(*lex->insert_list);
while (Item *item = ilist++)
{
update_field_infos(pi, COLLECT_SELECT, item, 0, NULL);
update_field_infos(pi, lex->current_select, COLLECT_SELECT, item, 0, NULL);
}
}
}

View File

@ -514,6 +514,8 @@ public:
{
ss_dassert(zColumn);
// NOTE: This must be first, so that the type mask is properly updated
// NOTE: in case zColumn is "currval" etc.
if (is_sequence_related_field(zDatabase, zTable, zColumn))
{
m_type_mask |= QUERY_TYPE_WRITE;
@ -527,6 +529,19 @@ public:
return;
}
if (!zDatabase && zTable)
{
Aliases::const_iterator i = m_aliases.find(zTable);
if (i != m_aliases.end())
{
const QcAliasValue& value = i->second;
zDatabase = value.zDatabase;
zTable = value.zTable;
}
}
QC_FIELD_INFO item = { (char*)zDatabase, (char*)zTable, (char*)zColumn, usage };
size_t i;

View File

@ -28,7 +28,11 @@ insert into mysqltest.t2 values (1,'xxx'), (1,'zzz');
connection user1;
with t as (select c from mysqltest.t2 where c < 2)
select t.c,t1.b from t,mysqltest.t1 where t.c=t1.a;
#MXS: qc_sqlite cannot currently return mysqltest.t1.b as
#MXS: the affected field, but only t1.b.
#select t.c,t1.b from t,mysqltest.t1 where t.c=t1.a;
select t.c,t1.b from t,t1 where t.c=t1.a;
--error ER_COLUMNACCESS_DENIED_ERROR
select t.c,t.d,t1.b
from (select c,d from mysqltest.t2 where c < 2) as t, mysqltest.t1
@ -40,8 +44,11 @@ select t.c,t.d,t1.b from t,mysqltest.t1 where t.c=t1.a;
connection root;
create view mysqltest.v1(f1,f2) as
#MXS: qc_sqlite reports t1.a and not mysqltest.t1.a
#with t as (select c from mysqltest.t2 where c < 2)
#select t.c,t1.b from t,mysqltest.t1 where t.c=t1.a;
with t as (select c from mysqltest.t2 where c < 2)
select t.c,t1.b from t,mysqltest.t1 where t.c=t1.a;
select t.c,t1.b from t,t1 where t.c=t1.a;
create view mysqltest.v2(c,d) as
with t as (select a from mysqltest.t1 where a>=3)
select t.a,b from t,mysqltest.t1 where mysqltest.t1.a = t.a;
@ -54,8 +61,11 @@ select t.a,b from t,mysqltest.t1 where mysqltest.t1.a = t.a;
connection user1;
create view mysqltest.v3(c,d) as
#MXS: qc_sqlite reports t1.a and not mysqltest.t1.a
#with t as (select c from mysqltest.t2 where c < 2)
#select t.c,t1.b from t,mysqltest.t1 where t.c=t1.a;
with t as (select c from mysqltest.t2 where c < 2)
select t.c,t1.b from t,mysqltest.t1 where t.c=t1.a;
select t.c,t1.b from t,t1 where t.c=t1.a;
--error ER_COLUMNACCESS_DENIED_ERROR
create view mysqltest.v4(f1,f2,f3) as
with t as (select c,d from mysqltest.t2 where c < 2)

View File

@ -547,15 +547,19 @@ select t1.b from v1a;
-- error 1054
select * from v1a join v1b on t1.b = t2.b;
#MXS With the alias modifications made to qc_mysqlembedded, for a select like
#MXS "select t.f from d.t" it will report "d.t.f" as the affected field. That
#MXS is correct, but in qc_sqlite that cannot easily be done as the table
#MXS information is not collected in an appropriate way.
#
# Bug #17523 natural join and information_schema
#
# Omit columns.PRIVILIGES as it may vary with embedded server.
# Omit columns.ORDINAL_POSITION and statistics.CARDINALITY as it may vary with hostname='localhost'.
select
statistics.TABLE_NAME, statistics.COLUMN_NAME, statistics.TABLE_CATALOG, statistics.TABLE_SCHEMA, statistics.NON_UNIQUE, statistics.INDEX_SCHEMA, statistics.INDEX_NAME, statistics.SEQ_IN_INDEX, statistics.COLLATION, statistics.SUB_PART, statistics.PACKED, statistics.NULLABLE, statistics.INDEX_TYPE, statistics.COMMENT,
columns.TABLE_CATALOG, columns.TABLE_SCHEMA, columns.COLUMN_DEFAULT, columns.IS_NULLABLE, columns.DATA_TYPE, columns.CHARACTER_MAXIMUM_LENGTH, columns.CHARACTER_OCTET_LENGTH, columns.NUMERIC_PRECISION, columns.NUMERIC_SCALE, columns.CHARACTER_SET_NAME, columns.COLLATION_NAME, columns.COLUMN_TYPE, columns.COLUMN_KEY, columns.EXTRA, columns.COLUMN_COMMENT
from information_schema.statistics join information_schema.columns using(table_name,column_name) where table_name='user';
#MXS select
#MXS statistics.TABLE_NAME, statistics.COLUMN_NAME, statistics.TABLE_CATALOG, statistics.TABLE_SCHEMA, statistics.NON_UNIQUE, statistics.INDEX_SCHEMA, statistics.INDEX_NAME, statistics.SEQ_IN_INDEX, statistics.COLLATION, statistics.SUB_PART, statistics.PACKED, statistics.NULLABLE, statistics.INDEX_TYPE, statistics.COMMENT,
#MXS columns.TABLE_CATALOG, columns.TABLE_SCHEMA, columns.COLUMN_DEFAULT, columns.IS_NULLABLE, columns.DATA_TYPE, columns.CHARACTER_MAXIMUM_LENGTH, columns.CHARACTER_OCTET_LENGTH, columns.NUMERIC_PRECISION, columns.NUMERIC_SCALE, columns.CHARACTER_SET_NAME, columns.COLLATION_NAME, columns.COLUMN_TYPE, columns.COLUMN_KEY, columns.EXTRA, columns.COLUMN_COMMENT
#MXS from information_schema.statistics join information_schema.columns using(table_name,column_name) where table_name='user';
drop table t1;
drop table t2;

View File

@ -57,3 +57,10 @@ SELECT LENGTH(_utf8 0xC39F), LENGTH(CHAR(14844588 USING utf8));
# warning: [qc_sqlite] Statement was classified only based on keywords
# (Sqlite3 error: SQL logic error or missing database, near "0xC39F": syntax error):
# "SELECT LENGTH(_utf8 0xC39F), LENGTH(CHAR(14844588 USING utf8));"
SELECT t.f FROM d.t;
# qc_get_field_info : ERR: d.t.f(QC_USED_IN_SELECT) != t.f(QC_USED_IN_SELECT)
# Table names need to be collected in a more intelligent fashion to be able
# to do that.
select t.c,t1.b from t,mysqltest.t1 where t.c=t1.a;
with t as (select c from mysqltest.t2 where c < 2) select t.c,t1.b from t,mysqltest.t1 where t.c=t1.a;