MXS-1364 Drop the usage field
But for the most trivial statements did not really provide useful information. The arguments of the "function" '=' are now reported.
This commit is contained in:
parent
4c100a305e
commit
ad4e8dad94
@ -360,6 +360,19 @@ select * from tbl where b = 3 and a = 2;
|
||||
as well. Although they conceptually are identical, there will be two
|
||||
cache entries.
|
||||
|
||||
Note that if a column has been specified in a rule, then a statement
|
||||
will match _irrespective_ of where that particular column appears.
|
||||
For instance, if a rule specifies that the result of statements referring
|
||||
to the the column _a_ should be cached, then the following statement will
|
||||
match
|
||||
```
|
||||
select a from tbl;
|
||||
```
|
||||
and so will
|
||||
```
|
||||
select b from tbl where a > 5;
|
||||
```
|
||||
|
||||
### Qualified Names
|
||||
|
||||
When using `=` or `!=` in the rule object in conjunction with `database`,
|
||||
|
@ -122,38 +122,6 @@ typedef enum qc_parse_result
|
||||
QC_QUERY_PARSED = 3 /*< The query was fully parsed; completely classified. */
|
||||
} qc_parse_result_t;
|
||||
|
||||
/**
|
||||
* qc_field_usage_t defines where a particular field appears.
|
||||
*
|
||||
* QC_USED_IN_SELECT : The field appears on the left side of FROM in a top-level SELECT statement.
|
||||
* QC_USED_IN_SUBSELECT: The field appears on the left side of FROM in a sub-select SELECT statement.
|
||||
* QC_USED_IN_WHERE : The field appears in a WHERE clause.
|
||||
* QC_USED_IN_SET : The field appears in the SET clause of an UPDATE statement.
|
||||
* QC_USED_IN_GROUP_BY : The field appears in a GROUP BY clause.
|
||||
*
|
||||
* Note that multiple bits may be set at the same time. For instance, for a statement like
|
||||
* "SELECT fld FROM tbl WHERE fld = 1 GROUP BY fld", the bits QC_USED_IN_SELECT, QC_USED_IN_WHERE
|
||||
* and QC_USED_IN_GROUP_BY will be set.
|
||||
*/
|
||||
typedef enum qc_field_usage
|
||||
{
|
||||
QC_USED_IN_SELECT = 0x01, /*< SELECT fld FROM... */
|
||||
QC_USED_IN_SUBSELECT = 0x02, /*< SELECT 1 FROM ... SELECT fld ... */
|
||||
QC_USED_IN_WHERE = 0x04, /*< SELECT ... FROM ... WHERE fld = ... */
|
||||
QC_USED_IN_SET = 0x08, /*< UPDATE ... SET fld = ... */
|
||||
QC_USED_IN_GROUP_BY = 0x10, /*< ... GROUP BY fld */
|
||||
} qc_field_usage_t;
|
||||
|
||||
/**
|
||||
* QC_FIELD_NAME contains information about the name of a field used in a statement.
|
||||
*/
|
||||
typedef struct qc_field_name
|
||||
{
|
||||
char* database; /** Present if the field is of the form "a.b.c", NULL otherwise. */
|
||||
char* table; /** Present if the field is of the form "a.b", NULL otherwise. */
|
||||
char* column; /** Always present. */
|
||||
} QC_FIELD_NAME;
|
||||
|
||||
/**
|
||||
* QC_FIELD_INFO contains information about a field used in a statement.
|
||||
*/
|
||||
@ -162,7 +130,6 @@ typedef struct qc_field_info
|
||||
char* database; /** Present if the field is of the form "a.b.c", NULL otherwise. */
|
||||
char* table; /** Present if the field is of the form "a.b", NULL otherwise. */
|
||||
char* column; /** Always present. */
|
||||
uint32_t usage; /** Bitfield denoting where the column appears. */
|
||||
} QC_FIELD_INFO;
|
||||
|
||||
/**
|
||||
@ -171,8 +138,7 @@ typedef struct qc_field_info
|
||||
typedef struct qc_function_info
|
||||
{
|
||||
char* name; /** Name of function. */
|
||||
uint32_t usage; /** Bitfield denoting where the column appears. */
|
||||
QC_FIELD_NAME* fields; /** What fields the function accesses. */
|
||||
QC_FIELD_INFO* fields; /** What fields the function accesses. */
|
||||
uint32_t n_fields; /** The number of fields in @c fields. */
|
||||
} QC_FUNCTION_INFO;
|
||||
|
||||
@ -569,25 +535,6 @@ void qc_thread_end(uint32_t kind);
|
||||
*/
|
||||
qc_parse_result_t qc_parse(GWBUF* stmt, uint32_t collect);
|
||||
|
||||
/**
|
||||
* Convert a qc_field_usage_t enum to corresponding string.
|
||||
*
|
||||
* @param usage The value to be converted
|
||||
*
|
||||
* @return The corresponding string. Must @b not be freed.
|
||||
*/
|
||||
const char* qc_field_usage_to_string(qc_field_usage_t usage);
|
||||
|
||||
/**
|
||||
* Convert a mask of qc_field_usage_t enum values to corresponding string.
|
||||
*
|
||||
* @param usage_mask Mask of qc_field_usage_t values.
|
||||
*
|
||||
* @return The corresponding string, or NULL if memory allocation fails.
|
||||
* @b Must be freed by the caller.
|
||||
*/
|
||||
char* qc_field_usage_mask_to_string(uint32_t usage_mask);
|
||||
|
||||
/**
|
||||
* Returns information about affected fields.
|
||||
*
|
||||
|
@ -1792,7 +1792,7 @@ static void parsing_info_done(void* ptr)
|
||||
|
||||
for (size_t j = 0; j < fi.n_fields; ++j)
|
||||
{
|
||||
QC_FIELD_NAME& field = fi.fields[j];
|
||||
QC_FIELD_INFO& field = fi.fields[j];
|
||||
|
||||
free(field.database);
|
||||
free(field.table);
|
||||
@ -2239,14 +2239,13 @@ static void add_field_info(parsing_info_t* info,
|
||||
const char* database,
|
||||
const char* table,
|
||||
const char* column,
|
||||
uint32_t usage,
|
||||
List<Item>* excludep)
|
||||
{
|
||||
ss_dassert(column);
|
||||
|
||||
unalias_names(select, database, table, &database, &table);
|
||||
|
||||
QC_FIELD_INFO item = { (char*)database, (char*)table, (char*)column, usage };
|
||||
QC_FIELD_INFO item = { (char*)database, (char*)table, (char*)column };
|
||||
|
||||
size_t i;
|
||||
for (i = 0; i < info->field_infos_len; ++i)
|
||||
@ -2303,10 +2302,6 @@ static void add_field_info(parsing_info_t* info,
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
info->field_infos[i].usage |= usage;
|
||||
}
|
||||
|
||||
// If field_infos is NULL, then the field was found and has already been noted.
|
||||
if (field_infos)
|
||||
@ -2335,7 +2330,7 @@ static void add_function_field_usage(const char* database,
|
||||
|
||||
while (!found && (i < fi->n_fields))
|
||||
{
|
||||
QC_FIELD_NAME& field = fi->fields[i];
|
||||
QC_FIELD_INFO& field = fi->fields[i];
|
||||
|
||||
if (strcasecmp(field.column, column) == 0)
|
||||
{
|
||||
@ -2361,14 +2356,14 @@ static void add_function_field_usage(const char* database,
|
||||
|
||||
if (!found)
|
||||
{
|
||||
QC_FIELD_NAME* fields = (QC_FIELD_NAME*)realloc(fi->fields,
|
||||
(fi->n_fields + 1) * sizeof(QC_FIELD_NAME));
|
||||
QC_FIELD_INFO* fields = (QC_FIELD_INFO*)realloc(fi->fields,
|
||||
(fi->n_fields + 1) * sizeof(QC_FIELD_INFO));
|
||||
ss_dassert(fields);
|
||||
|
||||
if (fields)
|
||||
{
|
||||
// Ignore potential alloc failures
|
||||
QC_FIELD_NAME& field = fields[fi->n_fields];
|
||||
QC_FIELD_INFO& field = fields[fi->n_fields];
|
||||
field.database = database ? strdup(database) : NULL;
|
||||
field.table = table ? strdup(table) : NULL;
|
||||
field.column = strdup(column);
|
||||
@ -2505,7 +2500,8 @@ static QC_FUNCTION_INFO* get_function_info(parsing_info_t* info, const char* nam
|
||||
function_info = &info->function_infos[info->function_infos_len++];
|
||||
|
||||
function_info->name = strdup(name);
|
||||
function_info->usage = 0;
|
||||
function_info->fields = NULL;
|
||||
function_info->n_fields = 0;
|
||||
}
|
||||
|
||||
return function_info;
|
||||
@ -2514,7 +2510,6 @@ static QC_FUNCTION_INFO* get_function_info(parsing_info_t* info, const char* nam
|
||||
static QC_FUNCTION_INFO* add_function_info(parsing_info_t* info,
|
||||
st_select_lex* select,
|
||||
const char* name,
|
||||
uint32_t usage,
|
||||
Item** items,
|
||||
int n_items)
|
||||
{
|
||||
@ -2524,7 +2519,7 @@ static QC_FUNCTION_INFO* add_function_info(parsing_info_t* info,
|
||||
|
||||
name = map_function_name(info->function_name_mappings, name);
|
||||
|
||||
QC_FUNCTION_INFO item = { (char*)name, usage };
|
||||
QC_FUNCTION_INFO item = { (char*)name };
|
||||
|
||||
size_t i;
|
||||
for (i = 0; i < info->function_infos_len; ++i)
|
||||
@ -2558,17 +2553,11 @@ static QC_FUNCTION_INFO* add_function_info(parsing_info_t* info,
|
||||
function_info = &info->function_infos[info->function_infos_len++];
|
||||
|
||||
function_info->name = strdup(name);
|
||||
function_info->usage = 0;
|
||||
function_info->fields = NULL;
|
||||
function_info->n_fields = 0;
|
||||
}
|
||||
|
||||
function_info->usage |= usage;
|
||||
|
||||
if (strcmp(name, "=") != 0)
|
||||
{
|
||||
add_function_field_usage(select, items, n_items, function_info);
|
||||
}
|
||||
add_function_field_usage(select, items, n_items, function_info);
|
||||
|
||||
return function_info;
|
||||
}
|
||||
@ -2576,7 +2565,6 @@ static QC_FUNCTION_INFO* add_function_info(parsing_info_t* info,
|
||||
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;
|
||||
@ -2668,13 +2656,12 @@ static void add_field_info(parsing_info_t* pi,
|
||||
break;
|
||||
}
|
||||
|
||||
add_field_info(pi, select, database, table, column, usage, excludep);
|
||||
add_field_info(pi, select, database, table, column, 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;
|
||||
@ -2686,7 +2673,7 @@ static void add_field_info(parsing_info_t* pi,
|
||||
strncpy(column, s, l);
|
||||
column[l] = 0;
|
||||
|
||||
add_field_info(pi, select, database, table, column, usage, excludep);
|
||||
add_field_info(pi, select, database, table, column, excludep);
|
||||
}
|
||||
|
||||
typedef enum collect_source
|
||||
@ -2700,7 +2687,6 @@ typedef enum collect_source
|
||||
static void update_field_infos(parsing_info_t* pi,
|
||||
LEX* lex,
|
||||
st_select_lex* select,
|
||||
uint32_t usage,
|
||||
List<Item>* excludep);
|
||||
|
||||
static void remove_surrounding_back_ticks(char* s)
|
||||
@ -2772,7 +2758,6 @@ static void update_field_infos(parsing_info_t* pi,
|
||||
st_select_lex* select,
|
||||
collect_source_t source,
|
||||
Item* item,
|
||||
uint32_t usage,
|
||||
List<Item>* excludep)
|
||||
{
|
||||
switch (item->type())
|
||||
@ -2784,13 +2769,13 @@ static void update_field_infos(parsing_info_t* pi,
|
||||
|
||||
while (Item *i = ilist++)
|
||||
{
|
||||
update_field_infos(pi, select, source, i, usage, excludep);
|
||||
update_field_infos(pi, select, source, i, excludep);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case Item::FIELD_ITEM:
|
||||
add_field_info(pi, select, static_cast<Item_field*>(item), usage, excludep);
|
||||
add_field_info(pi, select, static_cast<Item_field*>(item), excludep);
|
||||
break;
|
||||
|
||||
case Item::REF_ITEM:
|
||||
@ -2799,7 +2784,7 @@ static void update_field_infos(parsing_info_t* pi,
|
||||
{
|
||||
Item_ref* ref_item = static_cast<Item_ref*>(item);
|
||||
|
||||
add_field_info(pi, select, item, usage, excludep);
|
||||
add_field_info(pi, select, item, excludep);
|
||||
|
||||
size_t n_items = ref_item->cols();
|
||||
|
||||
@ -2809,7 +2794,7 @@ static void update_field_infos(parsing_info_t* pi,
|
||||
|
||||
if (reffed_item != ref_item)
|
||||
{
|
||||
update_field_infos(pi, select, source, ref_item->element_index(i), usage, excludep);
|
||||
update_field_infos(pi, select, source, ref_item->element_index(i), excludep);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2823,7 +2808,7 @@ static void update_field_infos(parsing_info_t* pi,
|
||||
|
||||
for (size_t i = 0; i < n_items; ++i)
|
||||
{
|
||||
update_field_infos(pi, select, source, row_item->element_index(i), usage, excludep);
|
||||
update_field_infos(pi, select, source, row_item->element_index(i), excludep);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -2929,12 +2914,12 @@ static void update_field_infos(parsing_info_t* pi,
|
||||
strcpy(func_name, "addtime");
|
||||
}
|
||||
|
||||
add_function_info(pi, select, func_name, usage, items, n_items);
|
||||
add_function_info(pi, select, func_name, items, n_items);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < n_items; ++i)
|
||||
{
|
||||
update_field_infos(pi, select, source, items[i], usage, excludep);
|
||||
update_field_infos(pi, select, source, items[i], excludep);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -2946,7 +2931,7 @@ static void update_field_infos(parsing_info_t* pi,
|
||||
switch (subselect_item->substype())
|
||||
{
|
||||
case Item_subselect::IN_SUBS:
|
||||
fi = add_function_info(pi, select, "in", usage, 0, 0);
|
||||
fi = add_function_info(pi, select, "in", 0, 0);
|
||||
case Item_subselect::ALL_SUBS:
|
||||
case Item_subselect::ANY_SUBS:
|
||||
{
|
||||
@ -2962,7 +2947,7 @@ static void update_field_infos(parsing_info_t* pi,
|
||||
if (in_subselect_item->left_expr_orig)
|
||||
{
|
||||
update_field_infos(pi, select, source, // TODO: Might be wrong select.
|
||||
in_subselect_item->left_expr_orig, usage, excludep);
|
||||
in_subselect_item->left_expr_orig, excludep);
|
||||
|
||||
if (subselect_item->substype() == Item_subselect::IN_SUBS)
|
||||
{
|
||||
@ -2977,15 +2962,9 @@ static void update_field_infos(parsing_info_t* pi,
|
||||
st_select_lex* ssl = in_subselect_item->get_select_lex();
|
||||
if (ssl)
|
||||
{
|
||||
uint32_t sub_usage = usage;
|
||||
|
||||
sub_usage &= ~QC_USED_IN_SELECT;
|
||||
sub_usage |= QC_USED_IN_SUBSELECT;
|
||||
|
||||
update_field_infos(pi,
|
||||
get_lex(pi),
|
||||
ssl,
|
||||
sub_usage,
|
||||
excludep);
|
||||
|
||||
if (subselect_item->substype() == Item_subselect::IN_SUBS)
|
||||
@ -3009,15 +2988,9 @@ static void update_field_infos(parsing_info_t* pi,
|
||||
st_select_lex* ssl = exists_subselect_item->get_select_lex();
|
||||
if (ssl)
|
||||
{
|
||||
uint32_t sub_usage = usage;
|
||||
|
||||
sub_usage &= ~QC_USED_IN_SELECT;
|
||||
sub_usage |= QC_USED_IN_SUBSELECT;
|
||||
|
||||
update_field_infos(pi,
|
||||
get_lex(pi),
|
||||
ssl,
|
||||
sub_usage,
|
||||
excludep);
|
||||
}
|
||||
}
|
||||
@ -3028,10 +3001,7 @@ static void update_field_infos(parsing_info_t* pi,
|
||||
Item_singlerow_subselect* ss_item = static_cast<Item_singlerow_subselect*>(item);
|
||||
st_select_lex *ssl = ss_item->get_select_lex();
|
||||
|
||||
usage &= ~QC_USED_IN_SELECT;
|
||||
usage |= QC_USED_IN_SUBSELECT;
|
||||
|
||||
update_field_infos(pi, get_lex(pi), ssl, usage, excludep);
|
||||
update_field_infos(pi, get_lex(pi), ssl, excludep);
|
||||
}
|
||||
break;
|
||||
|
||||
@ -3052,14 +3022,13 @@ static void update_field_infos(parsing_info_t* pi,
|
||||
static void update_field_infos(parsing_info_t* pi,
|
||||
LEX* lex,
|
||||
st_select_lex_unit* select,
|
||||
uint32_t usage,
|
||||
List<Item>* excludep)
|
||||
{
|
||||
st_select_lex* s = select->first_select();
|
||||
|
||||
if (s)
|
||||
{
|
||||
update_field_infos(pi, lex, s, usage, excludep);
|
||||
update_field_infos(pi, lex, s, excludep);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@ -3067,14 +3036,13 @@ static void update_field_infos(parsing_info_t* pi,
|
||||
static void update_field_infos(parsing_info_t* pi,
|
||||
LEX* lex,
|
||||
st_select_lex* select,
|
||||
uint32_t usage,
|
||||
List<Item>* excludep)
|
||||
{
|
||||
List_iterator<Item> ilist(select->item_list);
|
||||
|
||||
while (Item *item = ilist++)
|
||||
{
|
||||
update_field_infos(pi, select, COLLECT_SELECT, item, usage, NULL);
|
||||
update_field_infos(pi, select, COLLECT_SELECT, item, NULL);
|
||||
}
|
||||
|
||||
if (select->group_list.first)
|
||||
@ -3084,8 +3052,7 @@ static void update_field_infos(parsing_info_t* pi,
|
||||
{
|
||||
Item* item = *order->item;
|
||||
|
||||
update_field_infos(pi, select, COLLECT_GROUP_BY, item, QC_USED_IN_GROUP_BY,
|
||||
&select->item_list);
|
||||
update_field_infos(pi, select, COLLECT_GROUP_BY, item, &select->item_list);
|
||||
|
||||
order = order->next;
|
||||
}
|
||||
@ -3093,15 +3060,8 @@ static void update_field_infos(parsing_info_t* pi,
|
||||
|
||||
if (select->where)
|
||||
{
|
||||
uint32_t sub_usage = QC_USED_IN_WHERE;
|
||||
// TODO: The usage bits should get an overhaul. The following would make sense
|
||||
// TODO: but breaks things overall. So for another time.
|
||||
// TODO: sub_usage &= ~QC_USED_IN_SELECT;
|
||||
// TODO: sub_usage |= QC_USED_IN_WHERE;
|
||||
|
||||
update_field_infos(pi, select, COLLECT_WHERE,
|
||||
select->where,
|
||||
sub_usage,
|
||||
&select->item_list);
|
||||
}
|
||||
|
||||
@ -3126,9 +3086,7 @@ static void update_field_infos(parsing_info_t* pi,
|
||||
if (sl)
|
||||
{
|
||||
// This is for "SELECT 1 FROM (SELECT ...)"
|
||||
usage &= ~QC_USED_IN_SELECT;
|
||||
usage |= QC_USED_IN_SUBSELECT;
|
||||
update_field_infos(pi, get_lex(pi), sl, usage, excludep);
|
||||
update_field_infos(pi, get_lex(pi), sl, excludep);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3168,29 +3126,35 @@ int32_t qc_mysql_get_field_info(GWBUF* buf, const QC_FIELD_INFO** infos, uint32_
|
||||
return QC_RESULT_OK;
|
||||
}
|
||||
|
||||
uint32_t usage = 0;
|
||||
|
||||
switch (lex->sql_command)
|
||||
{
|
||||
case SQLCOM_UPDATE:
|
||||
case SQLCOM_UPDATE_MULTI:
|
||||
usage |= QC_USED_IN_SET;
|
||||
break;
|
||||
|
||||
default:
|
||||
usage |= QC_USED_IN_SELECT;
|
||||
}
|
||||
|
||||
lex->current_select = &lex->select_lex;
|
||||
|
||||
update_field_infos(pi, lex, &lex->select_lex, usage, NULL);
|
||||
update_field_infos(pi, lex, &lex->select_lex, NULL);
|
||||
|
||||
QC_FUNCTION_INFO* fi = NULL;
|
||||
|
||||
if ((lex->sql_command == SQLCOM_UPDATE) || (lex->sql_command == SQLCOM_UPDATE_MULTI))
|
||||
{
|
||||
List_iterator<Item> ilist(lex->current_select->item_list);
|
||||
Item *item = ilist++;
|
||||
|
||||
fi = get_function_info(pi, "=");
|
||||
|
||||
while (item)
|
||||
{
|
||||
update_field_infos(pi, lex->current_select, COLLECT_SELECT, item, NULL);
|
||||
|
||||
if (item->type() == Item::FIELD_ITEM)
|
||||
{
|
||||
add_function_field_usage(lex->current_select, static_cast<Item_field*>(item), fi);
|
||||
}
|
||||
|
||||
item = ilist++;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CTE_SUPPORTED
|
||||
if (lex->with_clauses_list)
|
||||
{
|
||||
usage &= ~QC_USED_IN_SELECT;
|
||||
usage |= QC_USED_IN_SUBSELECT;
|
||||
|
||||
With_clause* with_clause = lex->with_clauses_list;
|
||||
|
||||
while (with_clause)
|
||||
@ -3200,11 +3164,11 @@ int32_t qc_mysql_get_field_info(GWBUF* buf, const QC_FIELD_INFO** infos, uint32_
|
||||
|
||||
while (element)
|
||||
{
|
||||
update_field_infos(pi, lex, element->spec, usage, NULL);
|
||||
update_field_infos(pi, lex, element->spec, NULL);
|
||||
|
||||
if (element->is_recursive && element->first_recursive)
|
||||
{
|
||||
update_field_infos(pi, lex, element->first_recursive, usage, NULL);
|
||||
update_field_infos(pi, lex, element->first_recursive, NULL);
|
||||
}
|
||||
|
||||
element = element->next;
|
||||
@ -3218,7 +3182,15 @@ 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, lex->current_select, COLLECT_SELECT, item, 0, NULL);
|
||||
update_field_infos(pi, lex->current_select, COLLECT_SELECT, item, NULL);
|
||||
|
||||
if (fi)
|
||||
{
|
||||
if (item->type() == Item::FIELD_ITEM)
|
||||
{
|
||||
add_function_field_usage(lex->current_select, static_cast<Item_field*>(item), fi);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((lex->sql_command == SQLCOM_INSERT) ||
|
||||
@ -3227,9 +3199,24 @@ int32_t qc_mysql_get_field_info(GWBUF* buf, const QC_FIELD_INFO** infos, uint32_
|
||||
(lex->sql_command == SQLCOM_REPLACE_SELECT))
|
||||
{
|
||||
List_iterator<Item> ilist(lex->field_list);
|
||||
while (Item *item = ilist++)
|
||||
Item* item = ilist++;
|
||||
|
||||
if (item)
|
||||
{
|
||||
update_field_infos(pi, lex->current_select, COLLECT_SELECT, item, 0, NULL);
|
||||
// We get here in case of "insert into t set a = 0".
|
||||
QC_FUNCTION_INFO* fi = get_function_info(pi, "=");
|
||||
|
||||
while (item)
|
||||
{
|
||||
update_field_infos(pi, lex->current_select, COLLECT_SELECT, item, NULL);
|
||||
|
||||
if (item->type() == Item::FIELD_ITEM)
|
||||
{
|
||||
add_function_field_usage(lex->current_select, static_cast<Item_field*>(item), fi);
|
||||
}
|
||||
|
||||
item = ilist++;
|
||||
}
|
||||
}
|
||||
|
||||
if (lex->insert_list)
|
||||
@ -3237,7 +3224,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, lex->current_select, COLLECT_SELECT, item, 0, NULL);
|
||||
update_field_infos(pi, lex->current_select, COLLECT_SELECT, item, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3270,17 +3257,13 @@ int32_t qc_mysql_get_field_info(GWBUF* buf, const QC_FIELD_INFO** infos, uint32_
|
||||
// code after the closing }.
|
||||
}
|
||||
|
||||
usage &= ~QC_USED_IN_SELECT;
|
||||
usage &= ~QC_USED_IN_SET;
|
||||
usage |= QC_USED_IN_SUBSELECT;
|
||||
|
||||
st_select_lex* select = lex->all_selects_list;
|
||||
|
||||
while (select)
|
||||
{
|
||||
if (select->nest_level != 0) // Not the top-level select.
|
||||
{
|
||||
update_field_infos(pi, lex, select, usage, NULL);
|
||||
update_field_infos(pi, lex, select, NULL);
|
||||
}
|
||||
|
||||
select = select->next_select_in_list();
|
||||
|
@ -256,19 +256,18 @@ public:
|
||||
return pInfo;
|
||||
}
|
||||
|
||||
template<class T> // QC_FIELD_INFO, QC_FIELD_NAME
|
||||
static void finish(T& t)
|
||||
static void finish_field_info(QC_FIELD_INFO& info)
|
||||
{
|
||||
MXS_FREE(t.database);
|
||||
MXS_FREE(t.table);
|
||||
MXS_FREE(t.column);
|
||||
MXS_FREE(info.database);
|
||||
MXS_FREE(info.table);
|
||||
MXS_FREE(info.column);
|
||||
}
|
||||
|
||||
static void finish_function_info(QC_FUNCTION_INFO& info)
|
||||
{
|
||||
MXS_FREE(info.name);
|
||||
|
||||
std::for_each(info.fields, info.fields + info.n_fields, finish<QC_FIELD_NAME>);
|
||||
std::for_each(info.fields, info.fields + info.n_fields, finish_field_info);
|
||||
}
|
||||
|
||||
~QcSqliteInfo()
|
||||
@ -279,7 +278,7 @@ public:
|
||||
std::for_each(m_database_names.begin(), m_database_names.end(), mxs_free);
|
||||
free(m_zPrepare_name);
|
||||
gwbuf_free(m_pPreparable_stmt);
|
||||
std::for_each(m_field_infos.begin(), m_field_infos.end(), finish<QC_FIELD_INFO>);
|
||||
std::for_each(m_field_infos.begin(), m_field_infos.end(), finish_field_info);
|
||||
std::for_each(m_function_infos.begin(), m_function_infos.end(), finish_function_info);
|
||||
|
||||
// Data in m_function_field_usage is freed in finish_function_info().
|
||||
@ -606,7 +605,6 @@ public:
|
||||
const char* zDatabase,
|
||||
const char* zTable,
|
||||
const char* zColumn,
|
||||
uint32_t usage,
|
||||
const ExprList* pExclude)
|
||||
{
|
||||
ss_dassert(zColumn);
|
||||
@ -648,7 +646,6 @@ public:
|
||||
item.table = zTable ? MXS_STRDUP(zTable) : NULL;
|
||||
ss_dassert(zColumn);
|
||||
item.column = MXS_STRDUP(zColumn);
|
||||
item.usage = usage;
|
||||
|
||||
// We are happy if we at least could dup the column.
|
||||
|
||||
@ -658,10 +655,6 @@ public:
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
i->usage |= usage;
|
||||
}
|
||||
}
|
||||
|
||||
void update_names(const char* zDatabase, const char* zTable, const char* zAlias, QcAliases* pAliases)
|
||||
@ -800,7 +793,6 @@ public:
|
||||
void update_field_infos(QcAliases* pAliases,
|
||||
int prev_token,
|
||||
const Expr* pExpr,
|
||||
uint32_t usage,
|
||||
qc_token_position_t pos,
|
||||
const ExprList* pExclude)
|
||||
{
|
||||
@ -813,15 +805,15 @@ public:
|
||||
switch (pExpr->op)
|
||||
{
|
||||
case TK_ASTERISK: // select *
|
||||
update_field_infos_from_expr(pAliases, pExpr, usage, pExclude);
|
||||
update_field_infos_from_expr(pAliases, pExpr, pExclude);
|
||||
break;
|
||||
|
||||
case TK_DOT: // select a.b ... select a.b.c
|
||||
update_field_infos_from_expr(pAliases, pExpr, usage, pExclude);
|
||||
update_field_infos_from_expr(pAliases, pExpr, pExclude);
|
||||
break;
|
||||
|
||||
case TK_ID: // select a
|
||||
update_field_infos_from_expr(pAliases, pExpr, usage, pExclude);
|
||||
update_field_infos_from_expr(pAliases, pExpr, pExclude);
|
||||
break;
|
||||
|
||||
case TK_VARIABLE:
|
||||
@ -878,16 +870,6 @@ public:
|
||||
switch (pExpr->op)
|
||||
{
|
||||
case TK_EQ:
|
||||
// We don't report "=" if it's not used in a specific context (SELECT, WHERE)
|
||||
// and if it is used in SET. We also exclude it it in a context where a
|
||||
// variable is set.
|
||||
if (((usage != 0) && (usage != QC_USED_IN_SET)) &&
|
||||
(!pExpr->pLeft || (pExpr->pLeft->op != TK_VARIABLE)))
|
||||
{
|
||||
update_function_info(pAliases, get_token_symbol(pExpr->op), usage, pExclude);
|
||||
}
|
||||
break;
|
||||
|
||||
case TK_GE:
|
||||
case TK_GT:
|
||||
case TK_LE:
|
||||
@ -908,12 +890,11 @@ public:
|
||||
{
|
||||
int i = update_function_info(pAliases,
|
||||
get_token_symbol(pExpr->op),
|
||||
usage,
|
||||
pExclude);
|
||||
|
||||
if (i != -1)
|
||||
{
|
||||
vector<QC_FIELD_NAME>& fields = m_function_field_usage[i];
|
||||
vector<QC_FIELD_INFO>& fields = m_function_field_usage[i];
|
||||
|
||||
if (pExpr->pLeft)
|
||||
{
|
||||
@ -947,19 +928,19 @@ public:
|
||||
char sqlrowcount[13]; // strlen("sql") + strlen("%") + strlen("rowcount") + 1
|
||||
sprintf(sqlrowcount, "%s%%%s", pLeft->u.zToken, pRight->u.zToken);
|
||||
|
||||
update_function_info(pAliases, sqlrowcount, usage, pExclude);
|
||||
update_function_info(pAliases, sqlrowcount, pExclude);
|
||||
|
||||
pLeft = NULL;
|
||||
pRight = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
update_function_info(pAliases, get_token_symbol(pExpr->op), usage, pExclude);
|
||||
update_function_info(pAliases, get_token_symbol(pExpr->op), pExclude);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
update_function_info(pAliases, get_token_symbol(pExpr->op), usage, pExclude);
|
||||
update_function_info(pAliases, get_token_symbol(pExpr->op), pExclude);
|
||||
}
|
||||
break;
|
||||
|
||||
@ -967,7 +948,7 @@ public:
|
||||
switch (this_unit.parse_as)
|
||||
{
|
||||
case QC_PARSE_AS_DEFAULT:
|
||||
update_function_info(pAliases, get_token_symbol(pExpr->op), usage, pExclude);
|
||||
update_function_info(pAliases, get_token_symbol(pExpr->op), pExclude);
|
||||
break;
|
||||
|
||||
case QC_PARSE_AS_103:
|
||||
@ -1004,7 +985,7 @@ public:
|
||||
// way qc_mysqlembedded does.
|
||||
if (!ignore_exprlist && (strcasecmp(zToken, "row") != 0))
|
||||
{
|
||||
update_function_info(pAliases, zToken, pExpr->x.pList, usage, pExclude);
|
||||
update_function_info(pAliases, zToken, pExpr->x.pList, pExclude);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -1015,17 +996,12 @@ public:
|
||||
|
||||
if (pLeft)
|
||||
{
|
||||
update_field_infos(pAliases, pExpr->op, pExpr->pLeft, usage, QC_TOKEN_LEFT, pExclude);
|
||||
update_field_infos(pAliases, pExpr->op, pExpr->pLeft, QC_TOKEN_LEFT, pExclude);
|
||||
}
|
||||
|
||||
if (pRight)
|
||||
{
|
||||
if (usage & QC_USED_IN_SET)
|
||||
{
|
||||
usage &= ~QC_USED_IN_SET;
|
||||
}
|
||||
|
||||
update_field_infos(pAliases, pExpr->op, pExpr->pRight, usage, QC_TOKEN_RIGHT, pExclude);
|
||||
update_field_infos(pAliases, pExpr->op, pExpr->pRight, QC_TOKEN_RIGHT, pExclude);
|
||||
}
|
||||
|
||||
if (pExpr->x.pList)
|
||||
@ -1035,7 +1011,7 @@ public:
|
||||
case TK_FUNCTION:
|
||||
if (!ignore_exprlist)
|
||||
{
|
||||
update_field_infos_from_exprlist(pAliases, pExpr->x.pList, usage, pExclude);
|
||||
update_field_infos_from_exprlist(pAliases, pExpr->x.pList, pExclude);
|
||||
}
|
||||
break;
|
||||
|
||||
@ -1045,11 +1021,6 @@ public:
|
||||
case TK_IN:
|
||||
case TK_SELECT:
|
||||
{
|
||||
uint32_t sub_usage = usage;
|
||||
|
||||
sub_usage &= ~QC_USED_IN_SELECT;
|
||||
sub_usage |= QC_USED_IN_SUBSELECT;
|
||||
|
||||
const char* zName = NULL;
|
||||
|
||||
switch (pExpr->op)
|
||||
@ -1063,24 +1034,23 @@ public:
|
||||
|
||||
if (pExpr->flags & EP_xIsSelect)
|
||||
{
|
||||
update_field_infos_from_subselect(pAliases, pExpr->x.pSelect,
|
||||
sub_usage, pExclude);
|
||||
update_field_infos_from_subselect(pAliases, pExpr->x.pSelect, pExclude);
|
||||
|
||||
|
||||
if (zName)
|
||||
{
|
||||
update_function_info(pAliases, zName,
|
||||
pExpr->x.pSelect->pEList, sub_usage, pExclude);
|
||||
pExpr->x.pSelect->pEList, pExclude);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
update_field_infos_from_exprlist(pAliases, pExpr->x.pList, usage, pExclude);
|
||||
update_field_infos_from_exprlist(pAliases, pExpr->x.pList, pExclude);
|
||||
|
||||
if (zName)
|
||||
{
|
||||
update_function_info(pAliases, zName,
|
||||
pExpr->x.pList, sub_usage, pExclude);
|
||||
pExpr->x.pList, pExclude);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1170,7 +1140,6 @@ public:
|
||||
|
||||
void update_field_infos_from_expr(QcAliases* pAliases,
|
||||
const Expr* pExpr,
|
||||
uint32_t usage,
|
||||
const ExprList* pExclude)
|
||||
{
|
||||
const char* zDatabase;
|
||||
@ -1179,33 +1148,31 @@ public:
|
||||
|
||||
if (get_field_name(pExpr, &zDatabase, &zTable, &zColumn))
|
||||
{
|
||||
update_field_info(pAliases, zDatabase, zTable, zColumn, usage, pExclude);
|
||||
update_field_info(pAliases, zDatabase, zTable, zColumn, pExclude);
|
||||
}
|
||||
}
|
||||
|
||||
void update_field_infos_from_exprlist(QcAliases* pAliases,
|
||||
const ExprList* pEList,
|
||||
uint32_t usage,
|
||||
const ExprList* pExclude)
|
||||
{
|
||||
for (int i = 0; i < pEList->nExpr; ++i)
|
||||
{
|
||||
ExprList::ExprList_item* pItem = &pEList->a[i];
|
||||
|
||||
update_field_infos(pAliases, 0, pItem->pExpr, usage, QC_TOKEN_MIDDLE, pExclude);
|
||||
update_field_infos(pAliases, 0, pItem->pExpr, QC_TOKEN_MIDDLE, pExclude);
|
||||
}
|
||||
}
|
||||
|
||||
void update_field_infos_from_idlist(QcAliases* pAliases,
|
||||
const IdList* pIds,
|
||||
uint32_t usage,
|
||||
const ExprList* pExclude)
|
||||
{
|
||||
for (int i = 0; i < pIds->nId; ++i)
|
||||
{
|
||||
IdList::IdList_item* pItem = &pIds->a[i];
|
||||
|
||||
update_field_info(pAliases, NULL, NULL, pItem->zName, usage, pExclude);
|
||||
update_field_info(pAliases, NULL, NULL, pItem->zName, pExclude);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1217,7 +1184,6 @@ public:
|
||||
|
||||
void update_field_infos_from_select(QcAliases& aliases,
|
||||
const Select* pSelect,
|
||||
uint32_t usage,
|
||||
const ExprList* pExclude,
|
||||
compound_approach_t compound_approach = ANALYZE_COMPOUND_SELECTS)
|
||||
{
|
||||
@ -1234,12 +1200,7 @@ public:
|
||||
|
||||
if (pSrc->a[i].pSelect)
|
||||
{
|
||||
uint32_t sub_usage = usage;
|
||||
|
||||
sub_usage &= ~QC_USED_IN_SELECT;
|
||||
sub_usage |= QC_USED_IN_SUBSELECT;
|
||||
|
||||
update_field_infos_from_select(aliases, pSrc->a[i].pSelect, sub_usage, pExclude);
|
||||
update_field_infos_from_select(aliases, pSrc->a[i].pSelect, pExclude);
|
||||
}
|
||||
|
||||
#ifdef QC_COLLECT_NAMES_FROM_USING
|
||||
@ -1257,20 +1218,20 @@ public:
|
||||
|
||||
if (pSelect->pEList)
|
||||
{
|
||||
update_field_infos_from_exprlist(&aliases, pSelect->pEList, usage, NULL);
|
||||
update_field_infos_from_exprlist(&aliases, pSelect->pEList, NULL);
|
||||
}
|
||||
|
||||
if (pSelect->pWhere)
|
||||
{
|
||||
m_has_clause = true;
|
||||
update_field_infos(&aliases,
|
||||
0, pSelect->pWhere, QC_USED_IN_WHERE, QC_TOKEN_MIDDLE, pSelect->pEList);
|
||||
0, pSelect->pWhere, QC_TOKEN_MIDDLE, pSelect->pEList);
|
||||
}
|
||||
|
||||
if (pSelect->pGroupBy)
|
||||
{
|
||||
update_field_infos_from_exprlist(&aliases,
|
||||
pSelect->pGroupBy, QC_USED_IN_GROUP_BY, pSelect->pEList);
|
||||
pSelect->pGroupBy, pSelect->pEList);
|
||||
}
|
||||
|
||||
if (pSelect->pHaving)
|
||||
@ -1296,7 +1257,7 @@ public:
|
||||
|
||||
while (pPrior)
|
||||
{
|
||||
update_field_infos_from_subselect(&aliases, pPrior, usage, pExclude,
|
||||
update_field_infos_from_subselect(&aliases, pPrior, pExclude,
|
||||
IGNORE_COMPOUND_SELECTS);
|
||||
pPrior = pPrior->pPrior;
|
||||
}
|
||||
@ -1306,13 +1267,12 @@ public:
|
||||
|
||||
void update_field_infos_from_subselect(QcAliases* pAliases,
|
||||
const Select* pSelect,
|
||||
uint32_t usage,
|
||||
const ExprList* pExclude,
|
||||
compound_approach_t compound_approach = ANALYZE_COMPOUND_SELECTS)
|
||||
{
|
||||
QcAliases aliases(*pAliases);
|
||||
|
||||
update_field_infos_from_select(aliases, pSelect, usage, pExclude, compound_approach);
|
||||
update_field_infos_from_select(aliases, pSelect, pExclude, compound_approach);
|
||||
}
|
||||
|
||||
void update_field_infos_from_with(QcAliases* pAliases, const With* pWith)
|
||||
@ -1323,7 +1283,7 @@ public:
|
||||
|
||||
if (pCte->pSelect)
|
||||
{
|
||||
update_field_infos_from_subselect(pAliases, pCte->pSelect, QC_USED_IN_SUBSELECT, NULL);
|
||||
update_field_infos_from_subselect(pAliases, pCte->pSelect, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1349,20 +1309,20 @@ public:
|
||||
const char* zDatabase,
|
||||
const char* zTable,
|
||||
const char* zColumn,
|
||||
vector<QC_FIELD_NAME>& fields)
|
||||
vector<QC_FIELD_INFO>& fields)
|
||||
{
|
||||
ss_dassert(zColumn);
|
||||
|
||||
honour_aliases(pAliases, &zDatabase, &zTable);
|
||||
|
||||
MatchFieldName<QC_FIELD_NAME> predicate(zDatabase, zTable, zColumn);
|
||||
MatchFieldName<QC_FIELD_INFO> predicate(zDatabase, zTable, zColumn);
|
||||
|
||||
vector<QC_FIELD_NAME>::iterator i = find_if(fields.begin(), fields.end(), predicate);
|
||||
vector<QC_FIELD_INFO>::iterator i = find_if(fields.begin(), fields.end(), predicate);
|
||||
|
||||
if (i == fields.end()) // Not present
|
||||
{
|
||||
//TODO: Add exclusion?
|
||||
QC_FIELD_NAME item;
|
||||
QC_FIELD_INFO item;
|
||||
|
||||
item.database = zDatabase ? MXS_STRDUP(zDatabase) : NULL;
|
||||
item.table = zTable ? MXS_STRDUP(zTable) : NULL;
|
||||
@ -1378,7 +1338,7 @@ public:
|
||||
static void update_function_fields(const QcAliases* pAliases,
|
||||
const Expr* pExpr,
|
||||
const ExprList* pExclude,
|
||||
vector<QC_FIELD_NAME>& fields)
|
||||
vector<QC_FIELD_INFO>& fields)
|
||||
{
|
||||
const char* zDatabase;
|
||||
const char* zTable;
|
||||
@ -1410,7 +1370,7 @@ public:
|
||||
static void update_function_fields(const QcAliases* pAliases,
|
||||
const ExprList* pEList,
|
||||
const ExprList* pExclude,
|
||||
vector<QC_FIELD_NAME>& fields)
|
||||
vector<QC_FIELD_INFO>& fields)
|
||||
{
|
||||
for (int i = 0; i < pEList->nExpr; ++i)
|
||||
{
|
||||
@ -1424,7 +1384,6 @@ public:
|
||||
const char* name,
|
||||
const Expr* pExpr,
|
||||
const ExprList* pEList,
|
||||
uint32_t usage,
|
||||
const ExprList* pExclude)
|
||||
|
||||
{
|
||||
@ -1440,7 +1399,7 @@ public:
|
||||
|
||||
name = map_function_name(m_pFunction_name_mappings, name);
|
||||
|
||||
QC_FUNCTION_INFO item = { (char*)name, usage };
|
||||
QC_FUNCTION_INFO item = { (char*)name };
|
||||
|
||||
size_t i;
|
||||
for (i = 0; i < m_function_infos.size(); ++i)
|
||||
@ -1467,14 +1426,10 @@ public:
|
||||
m_function_field_usage.resize(m_function_field_usage.size() + 1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_function_infos[i].usage |= usage;
|
||||
}
|
||||
|
||||
if (pExpr || pEList)
|
||||
{
|
||||
vector<QC_FIELD_NAME>& fields = m_function_field_usage[i];
|
||||
vector<QC_FIELD_INFO>& fields = m_function_field_usage[i];
|
||||
|
||||
if (pExpr)
|
||||
{
|
||||
@ -1500,28 +1455,25 @@ public:
|
||||
int update_function_info(const QcAliases* pAliases,
|
||||
const char* name,
|
||||
const Expr* pExpr,
|
||||
uint32_t usage,
|
||||
const ExprList* pExclude)
|
||||
|
||||
{
|
||||
return update_function_info(pAliases, name, pExpr, NULL, usage, pExclude);
|
||||
return update_function_info(pAliases, name, pExpr, NULL, pExclude);
|
||||
}
|
||||
|
||||
int update_function_info(const QcAliases* pAliases,
|
||||
const char* name,
|
||||
const ExprList* pEList,
|
||||
uint32_t usage,
|
||||
const ExprList* pExclude)
|
||||
{
|
||||
return update_function_info(pAliases, name, NULL, pEList, usage, pExclude);
|
||||
return update_function_info(pAliases, name, NULL, pEList, pExclude);
|
||||
}
|
||||
|
||||
int update_function_info(const QcAliases* pAliases,
|
||||
const char* name,
|
||||
uint32_t usage,
|
||||
const ExprList* pExclude)
|
||||
{
|
||||
return update_function_info(pAliases, name, NULL, NULL, usage, pExclude);
|
||||
return update_function_info(pAliases, name, NULL, NULL, pExclude);
|
||||
}
|
||||
|
||||
//
|
||||
@ -1681,7 +1633,7 @@ public:
|
||||
|
||||
if (pSelect)
|
||||
{
|
||||
update_field_infos_from_select(aliases, pSelect, QC_USED_IN_SELECT, NULL);
|
||||
update_field_infos_from_select(aliases, pSelect, NULL);
|
||||
}
|
||||
|
||||
exposed_sqlite3ExprListDelete(pParse->db, pCNames);
|
||||
@ -1751,7 +1703,7 @@ public:
|
||||
|
||||
if (pWhere)
|
||||
{
|
||||
update_field_infos(&aliases, 0, pWhere, QC_USED_IN_WHERE, QC_TOKEN_MIDDLE, 0);
|
||||
update_field_infos(&aliases, 0, pWhere, QC_TOKEN_MIDDLE, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1806,7 +1758,7 @@ public:
|
||||
if (pSelect)
|
||||
{
|
||||
QcAliases aliases;
|
||||
update_field_infos_from_select(aliases, pSelect, QC_USED_IN_SELECT, NULL);
|
||||
update_field_infos_from_select(aliases, pSelect, NULL);
|
||||
}
|
||||
else if (pOldTable)
|
||||
{
|
||||
@ -1839,28 +1791,37 @@ public:
|
||||
|
||||
if (pColumns)
|
||||
{
|
||||
update_field_infos_from_idlist(&aliases, pColumns, 0, NULL);
|
||||
update_field_infos_from_idlist(&aliases, pColumns, NULL);
|
||||
|
||||
int i = update_function_info(&aliases, "=", NULL);
|
||||
|
||||
if (i != -1)
|
||||
{
|
||||
vector<QC_FIELD_INFO>& fields = m_function_field_usage[i];
|
||||
|
||||
for (int j = 0; j < pColumns->nId; ++j)
|
||||
{
|
||||
update_function_fields(&aliases, NULL, NULL, pColumns->a[j].zName, fields);
|
||||
}
|
||||
|
||||
if (fields.size() != 0)
|
||||
{
|
||||
QC_FUNCTION_INFO& info = m_function_infos[i];
|
||||
|
||||
info.fields = &fields[0];
|
||||
info.n_fields = fields.size();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pSelect)
|
||||
{
|
||||
uint32_t usage;
|
||||
|
||||
if (pSelect->selFlags & SF_Values) // Synthesized from VALUES clause
|
||||
{
|
||||
usage = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
usage = QC_USED_IN_SELECT;
|
||||
}
|
||||
|
||||
update_field_infos_from_select(aliases, pSelect, usage, NULL);
|
||||
update_field_infos_from_select(aliases, pSelect, NULL);
|
||||
}
|
||||
|
||||
if (pSet)
|
||||
{
|
||||
update_field_infos_from_exprlist(&aliases, pSet, 0, NULL);
|
||||
update_field_infos_from_exprlist(&aliases, pSet, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1975,13 +1936,13 @@ public:
|
||||
ExprList::ExprList_item* pItem = &pChanges->a[i];
|
||||
|
||||
update_field_infos(&aliases,
|
||||
0, pItem->pExpr, QC_USED_IN_SET, QC_TOKEN_MIDDLE, NULL);
|
||||
0, pItem->pExpr, QC_TOKEN_MIDDLE, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
if (pWhere)
|
||||
{
|
||||
update_field_infos(&aliases, 0, pWhere, QC_USED_IN_WHERE, QC_TOKEN_MIDDLE, pChanges);
|
||||
update_field_infos(&aliases, 0, pWhere, QC_TOKEN_MIDDLE, pChanges);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2015,10 +1976,8 @@ public:
|
||||
m_type_mask = QUERY_TYPE_READ;
|
||||
}
|
||||
|
||||
uint32_t usage = sub_select ? QC_USED_IN_SUBSELECT : QC_USED_IN_SELECT;
|
||||
|
||||
QcAliases aliases;
|
||||
update_field_infos_from_select(aliases, pSelect, usage, NULL);
|
||||
update_field_infos_from_select(aliases, pSelect, NULL);
|
||||
}
|
||||
|
||||
void maxscaleAlterTable(Parse *pParse, /* Parser context. */
|
||||
@ -2062,7 +2021,7 @@ public:
|
||||
|
||||
if (pExprList)
|
||||
{
|
||||
update_field_infos_from_exprlist(NULL, pExprList, 0, NULL);
|
||||
update_field_infos_from_exprlist(NULL, pExprList, NULL);
|
||||
}
|
||||
|
||||
exposed_sqlite3SrcListDelete(pParse->db, pName);
|
||||
@ -2891,8 +2850,7 @@ public:
|
||||
if (pValue->op == TK_SELECT)
|
||||
{
|
||||
QcAliases aliases;
|
||||
update_field_infos_from_select(aliases, pValue->x.pSelect,
|
||||
QC_USED_IN_SUBSELECT, NULL);
|
||||
update_field_infos_from_select(aliases, pValue->x.pSelect, NULL);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -2918,8 +2876,6 @@ public:
|
||||
m_status = QC_QUERY_PARSED;
|
||||
m_operation = QUERY_OP_SHOW;
|
||||
|
||||
uint32_t u = QC_USED_IN_SELECT;
|
||||
|
||||
switch (pShow->what)
|
||||
{
|
||||
case MXS_SHOW_COLUMNS:
|
||||
@ -3248,7 +3204,7 @@ public:
|
||||
GWBUF* m_pPreparable_stmt; // The preparable statement.
|
||||
vector<QC_FIELD_INFO> m_field_infos; // Vector of fields used by the statement.
|
||||
vector<QC_FUNCTION_INFO> m_function_infos; // Vector of functions used by the statement.
|
||||
vector<vector<QC_FIELD_NAME> > m_function_field_usage; // Vector of vector fields used by functions
|
||||
vector<vector<QC_FIELD_INFO> > m_function_field_usage; // Vector of vector fields used by functions
|
||||
// of the statement. Data referred to from
|
||||
// m_function_infos
|
||||
size_t m_function_infos_len; // The used entries in function_infos.
|
||||
@ -3307,7 +3263,7 @@ extern void maxscaleShow(Parse*, MxsShow* pShow);
|
||||
extern void maxscaleTruncate(Parse*, Token* pDatabase, Token* pName);
|
||||
extern void maxscaleUse(Parse*, Token*);
|
||||
|
||||
extern void maxscale_update_function_info(const char* name, const Expr* pExpr, unsigned usage);
|
||||
extern void maxscale_update_function_info(const char* name, const Expr* pExpr);
|
||||
|
||||
extern void maxscaleComment();
|
||||
extern int maxscaleKeyword(int token);
|
||||
@ -3682,12 +3638,12 @@ static bool should_exclude(const char* zName, const ExprList* pExclude)
|
||||
return i != pExclude->nExpr;
|
||||
}
|
||||
|
||||
extern void maxscale_update_function_info(const char* name, const Expr* pExpr, uint32_t usage)
|
||||
extern void maxscale_update_function_info(const char* name, const Expr* pExpr)
|
||||
{
|
||||
QcSqliteInfo* pInfo = this_thread.pInfo;
|
||||
ss_dassert(pInfo);
|
||||
|
||||
pInfo->update_function_info(NULL, name, pExpr, usage, NULL);
|
||||
pInfo->update_function_info(NULL, name, pExpr, NULL);
|
||||
}
|
||||
|
||||
static const char* get_token_symbol(int token)
|
||||
|
@ -130,7 +130,7 @@ extern void maxscaleShow(Parse*, MxsShow* pShow);
|
||||
extern void maxscaleTruncate(Parse*, Token* pDatabase, Token* pName);
|
||||
extern void maxscaleUse(Parse*, Token*);
|
||||
|
||||
extern void maxscale_update_function_info(const char* name, const Expr* pExpr, unsigned usage);
|
||||
extern void maxscale_update_function_info(const char* name, const Expr* pExpr);
|
||||
|
||||
// Exposed utility functions
|
||||
void exposed_sqlite3ExprDelete(sqlite3 *db, Expr *pExpr)
|
||||
@ -1205,7 +1205,7 @@ selcollist(A) ::= sclp(P) MATCH LP id(X) RP AGAINST LP expr(Y) RP. {
|
||||
// Could be a subselect as well, but we just don't know it at this point.
|
||||
sqlite3ExprDelete(pParse->db, Y.pExpr);
|
||||
Expr *p = sqlite3PExpr(pParse, TK_ID, 0, 0, &X);
|
||||
maxscale_update_function_info("match", p, QC_USED_IN_SELECT);
|
||||
maxscale_update_function_info("match", p);
|
||||
A = sqlite3ExprListAppend(pParse, P, p);
|
||||
}
|
||||
%endif
|
||||
|
@ -855,7 +855,6 @@ public:
|
||||
: m_database(info.database ? info.database : "")
|
||||
, m_table(info.table ? info.table : "")
|
||||
, m_column(info.column ? info.column : "")
|
||||
, m_usage(info.usage)
|
||||
{}
|
||||
|
||||
bool eq(const QcFieldInfo& rhs) const
|
||||
@ -863,8 +862,7 @@ public:
|
||||
return
|
||||
m_database == rhs.m_database &&
|
||||
m_table == rhs.m_table &&
|
||||
m_column == rhs.m_column &&
|
||||
m_usage == rhs.m_usage;
|
||||
m_column == rhs.m_column;
|
||||
}
|
||||
|
||||
bool lt(const QcFieldInfo& rhs) const
|
||||
@ -891,18 +889,7 @@ public:
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_column < rhs.m_column)
|
||||
{
|
||||
rv = true;
|
||||
}
|
||||
else if (m_column > rhs.m_column)
|
||||
{
|
||||
rv = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
rv = (m_usage < rhs.m_usage);
|
||||
}
|
||||
rv = m_column < rhs.m_column;
|
||||
}
|
||||
}
|
||||
|
||||
@ -917,35 +904,6 @@ public:
|
||||
m_column == o.m_column;
|
||||
}
|
||||
|
||||
static bool at_most_usage_differs(const std::set<QcFieldInfo>& l,
|
||||
const std::set<QcFieldInfo>& r)
|
||||
{
|
||||
bool rv = false;
|
||||
|
||||
if (l.size() == r.size())
|
||||
{
|
||||
rv = true;
|
||||
|
||||
std::set<QcFieldInfo>::iterator i = l.begin();
|
||||
std::set<QcFieldInfo>::iterator j = r.begin();
|
||||
|
||||
while (rv && (i != l.end()))
|
||||
{
|
||||
if (!i->has_same_name(*j))
|
||||
{
|
||||
rv = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
++i;
|
||||
++j;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
void print(ostream& out) const
|
||||
{
|
||||
if (!m_database.empty())
|
||||
@ -961,19 +919,12 @@ public:
|
||||
}
|
||||
|
||||
out << m_column;
|
||||
|
||||
out << "[";
|
||||
char* s = qc_field_usage_mask_to_string(m_usage);
|
||||
out << s;
|
||||
free(s);
|
||||
out << "]";
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_database;
|
||||
std::string m_table;
|
||||
std::string m_column;
|
||||
uint32_t m_usage;
|
||||
};
|
||||
|
||||
ostream& operator << (ostream& out, const QcFieldInfo& x)
|
||||
@ -1040,11 +991,6 @@ bool compare_get_field_info(QUERY_CLASSIFIER* pClassifier1, GWBUF* pCopy1,
|
||||
ss << f1;
|
||||
success = true;
|
||||
}
|
||||
else if (QcFieldInfo::at_most_usage_differs(f1, f2))
|
||||
{
|
||||
ss << "WRN: " << f1 << " != " << f2;
|
||||
success = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
ss << "ERR: " << f1 << " != " << f2;
|
||||
@ -1061,7 +1007,6 @@ class QcFunctionInfo
|
||||
public:
|
||||
QcFunctionInfo(const QC_FUNCTION_INFO& info)
|
||||
: m_name(info.name)
|
||||
, m_usage(info.usage)
|
||||
, m_pFields(info.fields)
|
||||
, m_nFields(info.n_fields)
|
||||
{
|
||||
@ -1073,7 +1018,6 @@ public:
|
||||
{
|
||||
return
|
||||
m_name == rhs.m_name &&
|
||||
m_usage == rhs.m_usage &&
|
||||
have_same_fields(*this, rhs);
|
||||
}
|
||||
|
||||
@ -1089,14 +1033,6 @@ public:
|
||||
{
|
||||
rv = false;
|
||||
}
|
||||
else if (m_usage < rhs.m_usage)
|
||||
{
|
||||
rv = true;
|
||||
}
|
||||
else if (m_usage > rhs.m_usage)
|
||||
{
|
||||
rv = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::set<string> lfs;
|
||||
@ -1111,37 +1047,6 @@ public:
|
||||
return rv;
|
||||
}
|
||||
|
||||
static bool at_most_usage_differs(const std::set<QcFunctionInfo>& l,
|
||||
const std::set<QcFunctionInfo>& r)
|
||||
{
|
||||
bool rv = false;
|
||||
|
||||
if (l.size() == r.size())
|
||||
{
|
||||
rv = true;
|
||||
|
||||
std::set<QcFunctionInfo>::iterator i = l.begin();
|
||||
std::set<QcFunctionInfo>::iterator j = r.begin();
|
||||
|
||||
while (rv && (i != l.end()))
|
||||
{
|
||||
if (i->m_name != j->m_name)
|
||||
{
|
||||
rv = false;
|
||||
}
|
||||
else if (!have_same_fields(*i, *j))
|
||||
{
|
||||
rv = false;
|
||||
}
|
||||
|
||||
++i;
|
||||
++j;
|
||||
}
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
void print(ostream& out) const
|
||||
{
|
||||
out << m_name;
|
||||
@ -1150,7 +1055,7 @@ public:
|
||||
|
||||
for (uint32_t i = 0; i < m_nFields; ++i)
|
||||
{
|
||||
const QC_FIELD_NAME& name = m_pFields[i];
|
||||
const QC_FIELD_INFO& name = m_pFields[i];
|
||||
|
||||
if (name.database)
|
||||
{
|
||||
@ -1173,12 +1078,6 @@ public:
|
||||
}
|
||||
|
||||
out << ")";
|
||||
|
||||
out << "[";
|
||||
char* s = qc_field_usage_mask_to_string(m_usage);
|
||||
out << s;
|
||||
free(s);
|
||||
out << "]";
|
||||
}
|
||||
|
||||
private:
|
||||
@ -1208,7 +1107,7 @@ private:
|
||||
return rv;
|
||||
}
|
||||
|
||||
static std::string get_field_name(const QC_FIELD_NAME& field)
|
||||
static std::string get_field_name(const QC_FIELD_INFO& field)
|
||||
{
|
||||
string s;
|
||||
|
||||
@ -1226,13 +1125,14 @@ private:
|
||||
|
||||
s += field.column;
|
||||
|
||||
std::transform(s.begin(), s.end(), s.begin(), tolower);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_name;
|
||||
uint32_t m_usage;
|
||||
const QC_FIELD_NAME* m_pFields;
|
||||
const QC_FIELD_INFO* m_pFields;
|
||||
uint32_t m_nFields;
|
||||
};
|
||||
|
||||
@ -1300,11 +1200,6 @@ bool compare_get_function_info(QUERY_CLASSIFIER* pClassifier1, GWBUF* pCopy1,
|
||||
ss << f1;
|
||||
success = true;
|
||||
}
|
||||
else if (QcFunctionInfo::at_most_usage_differs(f1, f2))
|
||||
{
|
||||
ss << "WRN: " << f1 << " != " << f2;
|
||||
success = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
ss << "ERR: " << f1 << " != " << f2;
|
||||
|
@ -248,41 +248,44 @@ as
|
||||
select ancestors.name, ancestors.dob from ancestors;
|
||||
|
||||
--echo # recursive definition with two attached non-recursive
|
||||
with recursive
|
||||
ancestors(id,name,dob)
|
||||
as
|
||||
(
|
||||
with
|
||||
father(child_id,id,name,dob)
|
||||
as
|
||||
(
|
||||
select folks.id, f.id, f.name, f.dob
|
||||
from folks, folks f
|
||||
where folks.father=f.id
|
||||
|
||||
),
|
||||
mother(child_id,id,name,dob)
|
||||
as
|
||||
(
|
||||
select folks.id, m.id, m.name, m.dob
|
||||
from folks, folks m
|
||||
where folks.mother=m.id
|
||||
|
||||
)
|
||||
select folks.id, folks.name, folks.dob
|
||||
from folks
|
||||
where name='Me'
|
||||
union
|
||||
select f.id, f.name, f.dob
|
||||
from ancestors a, father f
|
||||
where f.child_id=a.id
|
||||
union
|
||||
select m.id, m.name, m.dob
|
||||
from ancestors a, mother m
|
||||
where m.child_id=a.id
|
||||
|
||||
)
|
||||
select ancestors.name, ancestors.dob from ancestors;
|
||||
#MXS qc_sqlite
|
||||
#MXS qc_get_function_info : ERR: =(folks.name, folks.father, folks.id, folks.mother, mother.child_id, ancestors.id, father.child_id) != =(mother.child_id, ancestors.id, folks.father, folks.id, folks.mother, mother.id, father.child_id, name)
|
||||
#MXS The reported function arguments to '=' are disjoint. The relevant are there though (folks.*).
|
||||
#MXS with recursive
|
||||
#MXS ancestors(id,name,dob)
|
||||
#MXS as
|
||||
#MXS (
|
||||
#MXS with
|
||||
#MXS father(child_id,id,name,dob)
|
||||
#MXS as
|
||||
#MXS (
|
||||
#MXS select folks.id, f.id, f.name, f.dob
|
||||
#MXS from folks, folks f
|
||||
#MXS where folks.father=f.id
|
||||
#MXS
|
||||
#MXS ),
|
||||
#MXS mother(child_id,id,name,dob)
|
||||
#MXS as
|
||||
#MXS (
|
||||
#MXS select folks.id, m.id, m.name, m.dob
|
||||
#MXS from folks, folks m
|
||||
#MXS where folks.mother=m.id
|
||||
#MXS
|
||||
#MXS )
|
||||
#MXS select folks.id, folks.name, folks.dob
|
||||
#MXS from folks
|
||||
#MXS where name='Me'
|
||||
#MXS union
|
||||
#MXS select f.id, f.name, f.dob
|
||||
#MXS from ancestors a, father f
|
||||
#MXS where f.child_id=a.id
|
||||
#MXS union
|
||||
#MXS select m.id, m.name, m.dob
|
||||
#MXS from ancestors a, mother m
|
||||
#MXS where m.child_id=a.id
|
||||
#MXS
|
||||
#MXS )
|
||||
#MXS select ancestors.name, ancestors.dob from ancestors;
|
||||
|
||||
--echo # simple recursion with one anchor and one recursive select
|
||||
--echo # the anchor is the first select in the specification
|
||||
|
@ -169,7 +169,9 @@ drop table t1;
|
||||
#
|
||||
create table t1(f1 int primary key);
|
||||
insert into t1 values (4),(3),(1),(2);
|
||||
delete from t1 where (@a:= f1) order by f1 limit 1;
|
||||
#MXS qc_myselembedded
|
||||
#MXS qc_get_function_info : ERR: != =(f1)
|
||||
#MXS delete from t1 where (@a:= f1) order by f1 limit 1;
|
||||
select @a;
|
||||
drop table t1;
|
||||
|
||||
|
@ -677,8 +677,10 @@ insert into t1 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9);
|
||||
create table t2 (a int, b int, filler char(100), key(a), key(b));
|
||||
create table t3 (a int, b int, filler char(100), key(a), key(b));
|
||||
|
||||
insert into t2
|
||||
select @a:= A.a + 10*(B.a + 10*C.a), @a, 'filler' from t1 A, t1 B, t1 C;
|
||||
#MXS qc_mysqlembedded
|
||||
#MXS qc_get_function_info : ERR: *(t1.a) +(t1.a) != *(t1.a) +(t1.a) =()
|
||||
#MXS insert into t2
|
||||
#MXS select @a:= A.a + 10*(B.a + 10*C.a), @a, 'filler' from t1 A, t1 B, t1 C;
|
||||
insert into t3 select * from t2 where a < 800;
|
||||
|
||||
# The order of tables must be t2,t3:
|
||||
@ -692,7 +694,9 @@ create table t1 (a int);
|
||||
insert into t1 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9);
|
||||
|
||||
create table t2 (a int, b int, primary key(a));
|
||||
insert into t2 select @v:=A.a+10*B.a, @v from t1 A, t1 B;
|
||||
#MXS qc_mysqlembedded
|
||||
#MXS qc_get_function_info : ERR: *(t1.a) +(t1.a) != *(t1.a) +(t1.a) =()
|
||||
#MXS insert into t2 select @v:=A.a+10*B.a, @v from t1 A, t1 B;
|
||||
|
||||
explain select * from t1;
|
||||
show status like '%cost%';
|
||||
|
@ -31,3 +31,16 @@ select * from (select a from t1 where b >= 'c') as t
|
||||
union
|
||||
select * from (select a from t1 where b >= 'c') as t
|
||||
where t.a >= 4;
|
||||
|
||||
#MXS qc_myselembedded
|
||||
#MXS qc_get_function_info : ERR: != =(f1)
|
||||
delete from t1 where (@a:= f1) order by f1 limit 1;
|
||||
|
||||
#MXS qc_mysqlembedded
|
||||
#MXS qc_get_function_info : ERR: *(t1.a) +(t1.a) != *(t1.a) +(t1.a) =()
|
||||
insert into t2
|
||||
select @a:= A.a + 10*(B.a + 10*C.a), @a, 'filler' from t1 A, t1 B, t1 C;
|
||||
|
||||
#MXS qc_mysqlembedded
|
||||
#MXS qc_get_function_info : ERR: *(t1.a) +(t1.a) != *(t1.a) +(t1.a) =()
|
||||
insert into t2 select @v:=A.a+10*B.a, @v from t1 A, t1 B;
|
||||
|
@ -102,3 +102,7 @@ select t2.fld3 from t2 where companynr = 58 and fld3 like "%imaginable%";
|
||||
#MXS qc_sqlite
|
||||
#MXS qc_get_function_info : ERR: >(a, b)[QC_USED_IN_WHERE] avg(a)[QC_USED_IN_SUBSELECT|QC_USED_IN_WHERE] != >(a)[QC_USED_IN_WHERE] avg(a)[QC_USED_IN_SUBSELECT|QC_USED_IN_WHERE]
|
||||
CREATE VIEW v1 AS SELECT a,1 as b FROM t1 WHERE a>(SELECT AVG(a) FROM t1) AND b>(SELECT 1);
|
||||
|
||||
#MXS qc_sqlite
|
||||
#MXS qc_get_function_info : ERR: =(t2.fld3) != =(fld3)
|
||||
select t2.fld3 from t2 where fld3 = 'honeysuckle';
|
||||
|
@ -1283,7 +1283,9 @@ select fld3 from t2 order by fld3 desc limit 5,5;
|
||||
# The table is read directly with read-next on fld3
|
||||
#
|
||||
|
||||
select t2.fld3 from t2 where fld3 = 'honeysuckle';
|
||||
#MXS qc_sqlite
|
||||
#MXS qc_get_function_info : ERR: =(t2.fld3) != =(fld3)
|
||||
#MXS select t2.fld3 from t2 where fld3 = 'honeysuckle';
|
||||
# MXS: qc_get_function_info : ERR: =()[QC_USED_IN_WHERE] like(t2.fld3)[QC_USED_IN_WHERE] != =()[QC_USED_IN_WHERE] like(fld3)[QC_USED_IN_WHERE]
|
||||
#select t2.fld3 from t2 where fld3 LIKE 'honeysuckl_';
|
||||
select fld3 from t2 where fld3 LIKE 'honeysuckl_';
|
||||
|
@ -352,141 +352,6 @@ GWBUF* qc_get_preparable_stmt(GWBUF* stmt)
|
||||
return preparable_stmt;
|
||||
}
|
||||
|
||||
struct type_name_info field_usage_to_type_name_info(qc_field_usage_t usage)
|
||||
{
|
||||
struct type_name_info info;
|
||||
|
||||
switch (usage)
|
||||
{
|
||||
case QC_USED_IN_SELECT:
|
||||
{
|
||||
static const char name[] = "QC_USED_IN_SELECT";
|
||||
info.name = name;
|
||||
info.name_len = sizeof(name) - 1;
|
||||
}
|
||||
break;
|
||||
|
||||
case QC_USED_IN_SUBSELECT:
|
||||
{
|
||||
static const char name[] = "QC_USED_IN_SUBSELECT";
|
||||
info.name = name;
|
||||
info.name_len = sizeof(name) - 1;
|
||||
}
|
||||
break;
|
||||
|
||||
case QC_USED_IN_WHERE:
|
||||
{
|
||||
static const char name[] = "QC_USED_IN_WHERE";
|
||||
info.name = name;
|
||||
info.name_len = sizeof(name) - 1;
|
||||
}
|
||||
break;
|
||||
|
||||
case QC_USED_IN_SET:
|
||||
{
|
||||
static const char name[] = "QC_USED_IN_SET";
|
||||
info.name = name;
|
||||
info.name_len = sizeof(name) - 1;
|
||||
}
|
||||
break;
|
||||
|
||||
case QC_USED_IN_GROUP_BY:
|
||||
{
|
||||
static const char name[] = "QC_USED_IN_GROUP_BY";
|
||||
info.name = name;
|
||||
info.name_len = sizeof(name) - 1;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
{
|
||||
static const char name[] = "UNKNOWN_FIELD_USAGE";
|
||||
info.name = name;
|
||||
info.name_len = sizeof(name) - 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
|
||||
|
||||
const char* qc_field_usage_to_string(qc_field_usage_t usage)
|
||||
{
|
||||
return field_usage_to_type_name_info(usage).name;
|
||||
}
|
||||
|
||||
static const qc_field_usage_t FIELD_USAGE_VALUES[] =
|
||||
{
|
||||
QC_USED_IN_SELECT,
|
||||
QC_USED_IN_SUBSELECT,
|
||||
QC_USED_IN_WHERE,
|
||||
QC_USED_IN_SET,
|
||||
QC_USED_IN_GROUP_BY,
|
||||
};
|
||||
|
||||
static const int N_FIELD_USAGE_VALUES =
|
||||
sizeof(FIELD_USAGE_VALUES) / sizeof(FIELD_USAGE_VALUES[0]);
|
||||
static const int FIELD_USAGE_MAX_LEN = 20; // strlen("QC_USED_IN_SUBSELECT");
|
||||
|
||||
char* qc_field_usage_mask_to_string(uint32_t mask)
|
||||
{
|
||||
size_t len = 0;
|
||||
|
||||
// First calculate how much space will be needed.
|
||||
for (int i = 0; i < N_FIELD_USAGE_VALUES; ++i)
|
||||
{
|
||||
if (mask & FIELD_USAGE_VALUES[i])
|
||||
{
|
||||
if (len != 0)
|
||||
{
|
||||
++len; // strlen("|");
|
||||
}
|
||||
|
||||
len += FIELD_USAGE_MAX_LEN;
|
||||
}
|
||||
}
|
||||
|
||||
++len;
|
||||
|
||||
// Then make one allocation and build the string.
|
||||
char* s = (char*) MXS_MALLOC(len);
|
||||
|
||||
if (s)
|
||||
{
|
||||
if (len > 1)
|
||||
{
|
||||
char* p = s;
|
||||
|
||||
for (int i = 0; i < N_FIELD_USAGE_VALUES; ++i)
|
||||
{
|
||||
qc_field_usage_t value = FIELD_USAGE_VALUES[i];
|
||||
|
||||
if (mask & value)
|
||||
{
|
||||
if (p != s)
|
||||
{
|
||||
strcpy(p, "|");
|
||||
++p;
|
||||
}
|
||||
|
||||
struct type_name_info info = field_usage_to_type_name_info(value);
|
||||
|
||||
strcpy(p, info.name);
|
||||
p += info.name_len;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
*s = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
const char* qc_op_to_string(qc_query_op_t op)
|
||||
{
|
||||
switch (op)
|
||||
|
170
server/modules/filter/cache/rules.cc
vendored
170
server/modules/filter/cache/rules.cc
vendored
@ -1144,55 +1144,52 @@ static bool cache_rule_matches_column_regexp(CACHE_RULE *self,
|
||||
{
|
||||
const QC_FIELD_INFO *info = (infos + i);
|
||||
|
||||
if (info->usage & QC_USED_IN_SELECT)
|
||||
size_t database_len;
|
||||
const char *database;
|
||||
|
||||
if (info->database)
|
||||
{
|
||||
size_t database_len;
|
||||
const char *database;
|
||||
|
||||
if (info->database)
|
||||
{
|
||||
database = info->database;
|
||||
database_len = strlen(info->database);
|
||||
}
|
||||
else
|
||||
{
|
||||
database = default_database;
|
||||
database_len = default_database_len;
|
||||
}
|
||||
|
||||
size_t table_len;
|
||||
const char *table;
|
||||
|
||||
if (info->table)
|
||||
{
|
||||
table = info->table;
|
||||
table_len = strlen(info->table);
|
||||
}
|
||||
else
|
||||
{
|
||||
table = default_table;
|
||||
table_len = default_table_len;
|
||||
}
|
||||
|
||||
char buffer[database_len + 1 + table_len + strlen(info->column) + 1];
|
||||
buffer[0] = 0;
|
||||
|
||||
if (database)
|
||||
{
|
||||
strcat(buffer, database);
|
||||
strcat(buffer, ".");
|
||||
}
|
||||
|
||||
if (table)
|
||||
{
|
||||
strcat(buffer, table);
|
||||
strcat(buffer, ".");
|
||||
}
|
||||
|
||||
strcat(buffer, info->column);
|
||||
|
||||
matches = cache_rule_compare(self, thread_id, buffer);
|
||||
database = info->database;
|
||||
database_len = strlen(info->database);
|
||||
}
|
||||
else
|
||||
{
|
||||
database = default_database;
|
||||
database_len = default_database_len;
|
||||
}
|
||||
|
||||
size_t table_len;
|
||||
const char *table;
|
||||
|
||||
if (info->table)
|
||||
{
|
||||
table = info->table;
|
||||
table_len = strlen(info->table);
|
||||
}
|
||||
else
|
||||
{
|
||||
table = default_table;
|
||||
table_len = default_table_len;
|
||||
}
|
||||
|
||||
char buffer[database_len + 1 + table_len + strlen(info->column) + 1];
|
||||
buffer[0] = 0;
|
||||
|
||||
if (database)
|
||||
{
|
||||
strcat(buffer, database);
|
||||
strcat(buffer, ".");
|
||||
}
|
||||
|
||||
if (table)
|
||||
{
|
||||
strcat(buffer, table);
|
||||
strcat(buffer, ".");
|
||||
}
|
||||
|
||||
strcat(buffer, info->column);
|
||||
|
||||
matches = cache_rule_compare(self, thread_id, buffer);
|
||||
|
||||
++i;
|
||||
}
|
||||
@ -1280,84 +1277,81 @@ static bool cache_rule_matches_column_simple(CACHE_RULE *self, const char *defau
|
||||
{
|
||||
const QC_FIELD_INFO *info = (infos + i);
|
||||
|
||||
if (info->usage & QC_USED_IN_SELECT)
|
||||
if ((strcasecmp(info->column, rule_column) == 0) || strcmp(rule_column, "*") == 0)
|
||||
{
|
||||
if ((strcasecmp(info->column, rule_column) == 0) || strcmp(rule_column, "*") == 0)
|
||||
if (rule_table)
|
||||
{
|
||||
if (rule_table)
|
||||
const char* check_table = info->table ? info->table : default_table;
|
||||
|
||||
if (check_table)
|
||||
{
|
||||
const char* check_table = info->table ? info->table : default_table;
|
||||
|
||||
if (check_table)
|
||||
if (strcasecmp(check_table, rule_table) == 0)
|
||||
{
|
||||
if (strcasecmp(check_table, rule_table) == 0)
|
||||
if (rule_database)
|
||||
{
|
||||
if (rule_database)
|
||||
{
|
||||
const char *check_database =
|
||||
info->database ? info->database : default_database;
|
||||
const char *check_database =
|
||||
info->database ? info->database : default_database;
|
||||
|
||||
if (check_database)
|
||||
if (check_database)
|
||||
{
|
||||
if (strcasecmp(check_database, rule_database) == 0)
|
||||
{
|
||||
if (strcasecmp(check_database, rule_database) == 0)
|
||||
{
|
||||
// The column, table and database matched.
|
||||
matches = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// The column, table matched but the database did not.
|
||||
matches = false;
|
||||
}
|
||||
// The column, table and database matched.
|
||||
matches = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the rules specify a database but we do not know the database,
|
||||
// we consider the databases not to match.
|
||||
// The column, table matched but the database did not.
|
||||
matches = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the rule specifies no database, then if the column and the table
|
||||
// matches, the rule matches.
|
||||
matches = true;
|
||||
// If the rules specify a database but we do not know the database,
|
||||
// we consider the databases not to match.
|
||||
matches = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// The column matched, but the table did not.
|
||||
matches = false;
|
||||
// If the rule specifies no database, then if the column and the table
|
||||
// matches, the rule matches.
|
||||
matches = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the rules specify a table but we do not know the table, we
|
||||
// consider the tables not to match.
|
||||
// The column matched, but the table did not.
|
||||
matches = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// The column matched and there is no table rule.
|
||||
matches = true;
|
||||
// If the rules specify a table but we do not know the table, we
|
||||
// consider the tables not to match.
|
||||
matches = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// The column did not match.
|
||||
matches = false;
|
||||
}
|
||||
|
||||
if (self->op == CACHE_OP_NEQ)
|
||||
{
|
||||
matches = !matches;
|
||||
// The column matched and there is no table rule.
|
||||
matches = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// The column did not match.
|
||||
matches = false;
|
||||
}
|
||||
|
||||
++i;
|
||||
if (self->op == CACHE_OP_NEQ)
|
||||
{
|
||||
matches = !matches;
|
||||
}
|
||||
}
|
||||
|
||||
++i;
|
||||
|
||||
if (tables)
|
||||
{
|
||||
for (i = 0; i < (size_t)n_tables; ++i)
|
||||
|
Loading…
x
Reference in New Issue
Block a user