MXS-2397 Provide context information for fields

The query classifier now returns contextual information for a fields;
does it appear in the (right hand side) of a UNION or in a SUBQUERY.
This commit is contained in:
Johan Wikman 2019-03-21 13:58:52 +02:00
parent fadbdc7514
commit f37340e9fd
3 changed files with 115 additions and 36 deletions

View File

@ -127,13 +127,23 @@ typedef enum qc_parse_result
} qc_parse_result_t;
/**
* QC_FIELD_INFO contains information about a field used in a statement.
* qc_field_context_t defines the context where a field appears.
*
* NOTE: A particular bit does NOT mean that the field appears ONLY in the context,
* but it may appear in other contexts as well.
*/
typedef enum qc_field_context
{
QC_FIELD_UNION = 1, /** The field appears on the right hand side in a UNION. */
QC_FIELD_SUBQUERY = 2 /** The field appears in a subquery. */
} qc_field_context_t;
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. */
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 context; /** The context in which the field appears. */
} QC_FIELD_INFO;
/**

View File

@ -636,6 +636,7 @@ public:
};
void update_field_info(const QcAliases* pAliases,
uint32_t context,
const char* zDatabase,
const char* zTable,
const char* zColumn,
@ -681,6 +682,7 @@ public:
item.table = zTable ? MXS_STRDUP(zTable) : NULL;
mxb_assert(zColumn);
item.column = MXS_STRDUP(zColumn);
item.context = context;
// We are happy if we at least could dup the column.
@ -690,6 +692,10 @@ public:
}
}
}
else
{
i->context |= context;
}
}
void update_names(const char* zDatabase, const char* zTable, const char* zAlias, QcAliases* pAliases)
@ -799,6 +805,7 @@ public:
}
void update_field_infos(QcAliases* pAliases,
uint32_t context,
int prev_token,
const Expr* pExpr,
qc_token_position_t pos,
@ -813,15 +820,15 @@ public:
switch (pExpr->op)
{
case TK_ASTERISK: // select *
update_field_infos_from_expr(pAliases, pExpr, pExclude);
update_field_infos_from_expr(pAliases, context, pExpr, pExclude);
break;
case TK_DOT: // select a.b ... select a.b.c
update_field_infos_from_expr(pAliases, pExpr, pExclude);
update_field_infos_from_expr(pAliases, context, pExpr, pExclude);
break;
case TK_ID: // select a
update_field_infos_from_expr(pAliases, pExpr, pExclude);
update_field_infos_from_expr(pAliases, context, pExpr, pExclude);
break;
case TK_VARIABLE:
@ -1008,12 +1015,12 @@ public:
if (pLeft)
{
update_field_infos(pAliases, pExpr->op, pExpr->pLeft, QC_TOKEN_LEFT, pExclude);
update_field_infos(pAliases, context, pExpr->op, pExpr->pLeft, QC_TOKEN_LEFT, pExclude);
}
if (pRight)
{
update_field_infos(pAliases, pExpr->op, pExpr->pRight, QC_TOKEN_RIGHT, pExclude);
update_field_infos(pAliases, context, pExpr->op, pExpr->pRight, QC_TOKEN_RIGHT, pExclude);
}
if (pExpr->x.pList)
@ -1023,7 +1030,7 @@ public:
case TK_FUNCTION:
if (!ignore_exprlist)
{
update_field_infos_from_exprlist(pAliases, pExpr->x.pList, pExclude);
update_field_infos_from_exprlist(pAliases, context, pExpr->x.pList, pExclude);
}
break;
@ -1047,7 +1054,7 @@ public:
if (pExpr->flags & EP_xIsSelect)
{
mxb_assert(pAliases);
update_field_infos_from_subselect(*pAliases, pExpr->x.pSelect, pExclude);
update_field_infos_from_subselect(*pAliases, context, pExpr->x.pSelect, pExclude);
if (zName)
@ -1060,7 +1067,7 @@ public:
}
else
{
update_field_infos_from_exprlist(pAliases, pExpr->x.pList, pExclude);
update_field_infos_from_exprlist(pAliases, context, pExpr->x.pList, pExclude);
if (zName)
{
@ -1152,6 +1159,7 @@ public:
}
void update_field_infos_from_expr(QcAliases* pAliases,
uint32_t context,
const Expr* pExpr,
const ExprList* pExclude)
{
@ -1163,12 +1171,13 @@ public:
{
if (get_field_name(pExpr, &zDatabase, &zTable, &zColumn))
{
update_field_info(pAliases, zDatabase, zTable, zColumn, pExclude);
update_field_info(pAliases, context, zDatabase, zTable, zColumn, pExclude);
}
}
}
void update_field_infos_from_exprlist(QcAliases* pAliases,
uint32_t context,
const ExprList* pEList,
const ExprList* pExclude)
{
@ -1176,11 +1185,12 @@ public:
{
ExprList::ExprList_item* pItem = &pEList->a[i];
update_field_infos(pAliases, 0, pItem->pExpr, QC_TOKEN_MIDDLE, pExclude);
update_field_infos(pAliases, context, 0, pItem->pExpr, QC_TOKEN_MIDDLE, pExclude);
}
}
void update_field_infos_from_idlist(QcAliases* pAliases,
uint32_t context,
const IdList* pIds,
const ExprList* pExclude)
{
@ -1190,7 +1200,7 @@ public:
{
IdList::IdList_item* pItem = &pIds->a[i];
update_field_info(pAliases, NULL, NULL, pItem->zName, pExclude);
update_field_info(pAliases, context, NULL, NULL, pItem->zName, pExclude);
}
}
}
@ -1202,6 +1212,7 @@ public:
};
void update_field_infos_from_select(QcAliases& aliases,
uint32_t context,
const Select* pSelect,
const ExprList* pExclude,
compound_approach_t compound_approach = ANALYZE_COMPOUND_SELECTS)
@ -1219,7 +1230,7 @@ public:
if (pSrc->a[i].pSelect)
{
update_field_infos_from_select(aliases, pSrc->a[i].pSelect, pExclude);
update_field_infos_from_select(aliases, context | QC_FIELD_SUBQUERY, pSrc->a[i].pSelect, pExclude);
}
#ifdef QC_COLLECT_NAMES_FROM_USING
@ -1237,13 +1248,14 @@ public:
if (pSelect->pEList)
{
update_field_infos_from_exprlist(&aliases, pSelect->pEList, NULL);
update_field_infos_from_exprlist(&aliases, context, pSelect->pEList, NULL);
}
if (pSelect->pWhere)
{
m_has_clause = true;
update_field_infos(&aliases,
context,
0,
pSelect->pWhere,
QC_TOKEN_MIDDLE,
@ -1253,6 +1265,7 @@ public:
if (pSelect->pGroupBy)
{
update_field_infos_from_exprlist(&aliases,
context,
pSelect->pGroupBy,
pSelect->pEList);
}
@ -1269,7 +1282,7 @@ public:
if (pSelect->pWith)
{
update_field_infos_from_with(&aliases, pSelect->pWith);
update_field_infos_from_with(&aliases, context, pSelect->pWith);
}
if (compound_approach == ANALYZE_COMPOUND_SELECTS)
@ -1280,10 +1293,22 @@ public:
while (pPrior)
{
update_field_infos_from_subselect(aliases,
pPrior,
pExclude,
IGNORE_COMPOUND_SELECTS);
uint32_t ctx = context;
if (!pPrior->pPrior)
{
// The fields in the first select in a UNION are not considered to
// be in a union. Those names will be visible in the resultset.
ctx &= ~QC_FIELD_UNION;
}
QcAliases aliases2(aliases);
update_field_infos_from_select(aliases2,
ctx,
pPrior,
pExclude,
IGNORE_COMPOUND_SELECTS);
pPrior = pPrior->pPrior;
}
}
@ -1291,16 +1316,19 @@ public:
}
void update_field_infos_from_subselect(const QcAliases& existing_aliases,
uint32_t context,
const Select* pSelect,
const ExprList* pExclude,
compound_approach_t compound_approach = ANALYZE_COMPOUND_SELECTS)
{
QcAliases aliases(existing_aliases);
update_field_infos_from_select(aliases, pSelect, pExclude, compound_approach);
context |= QC_FIELD_SUBQUERY;
update_field_infos_from_select(aliases, context, pSelect, pExclude, compound_approach);
}
void update_field_infos_from_with(QcAliases* pAliases, const With* pWith)
void update_field_infos_from_with(QcAliases* pAliases, uint32_t context, const With* pWith)
{
for (int i = 0; i < pWith->nCte; ++i)
{
@ -1309,7 +1337,7 @@ public:
if (pCte->pSelect)
{
mxb_assert(pAliases);
update_field_infos_from_subselect(*pAliases, pCte->pSelect, NULL);
update_field_infos_from_subselect(*pAliases, context, pCte->pSelect, NULL);
}
}
}
@ -1669,7 +1697,8 @@ public:
if (pSelect)
{
update_field_infos_from_select(aliases, pSelect, NULL);
uint32_t context = 0;
update_field_infos_from_select(aliases, context, pSelect, NULL);
}
exposed_sqlite3ExprListDelete(pParse->db, pCNames);
@ -1739,7 +1768,8 @@ public:
if (pWhere)
{
update_field_infos(&aliases, 0, pWhere, QC_TOKEN_MIDDLE, 0);
uint32_t context = 0;
update_field_infos(&aliases, context, 0, pWhere, QC_TOKEN_MIDDLE, 0);
}
}
@ -1794,7 +1824,8 @@ public:
if (pSelect)
{
QcAliases aliases;
update_field_infos_from_select(aliases, pSelect, NULL);
uint32_t context = 0;
update_field_infos_from_select(aliases, context, pSelect, NULL);
}
else if (pOldTable)
{
@ -1822,12 +1853,13 @@ public:
mxb_assert(pTabList->nSrc >= 1);
QcAliases aliases;
uint32_t context = 0;
update_names_from_srclist(&aliases, pTabList);
if (pColumns)
{
update_field_infos_from_idlist(&aliases, pColumns, NULL);
update_field_infos_from_idlist(&aliases, context, pColumns, NULL);
int i = update_function_info(&aliases, "=", NULL);
@ -1852,12 +1884,12 @@ public:
if (pSelect)
{
update_field_infos_from_select(aliases, pSelect, NULL);
update_field_infos_from_select(aliases, context, pSelect, NULL);
}
if (pSet)
{
update_field_infos_from_exprlist(&aliases, pSet, NULL);
update_field_infos_from_exprlist(&aliases, context, pSet, NULL);
}
}
@ -1959,6 +1991,7 @@ public:
if (m_operation != QUERY_OP_EXPLAIN)
{
QcAliases aliases;
uint32_t context = 0;
m_type_mask = QUERY_TYPE_WRITE;
m_operation = QUERY_OP_UPDATE;
@ -1972,6 +2005,7 @@ public:
ExprList::ExprList_item* pItem = &pChanges->a[i];
update_field_infos(&aliases,
context,
0,
pItem->pExpr,
QC_TOKEN_MIDDLE,
@ -1981,7 +2015,7 @@ public:
if (pWhere)
{
update_field_infos(&aliases, 0, pWhere, QC_TOKEN_MIDDLE, pChanges);
update_field_infos(&aliases, context, 0, pWhere, QC_TOKEN_MIDDLE, pChanges);
}
}
@ -2040,7 +2074,8 @@ public:
}
QcAliases aliases;
update_field_infos_from_select(aliases, pSelect, NULL);
uint32_t context = (pSelect->op == TK_UNION && pSelect->pPrior) ? QC_FIELD_UNION : 0;
update_field_infos_from_select(aliases, context, pSelect, NULL);
}
void maxscaleAlterTable(Parse* pParse, /* Parser context. */
@ -2086,7 +2121,8 @@ public:
if (pExprList)
{
QcAliases aliases;
update_field_infos_from_exprlist(&aliases, pExprList, NULL);
uint32_t context = 0;
update_field_infos_from_exprlist(&aliases, context, pExprList, NULL);
}
exposed_sqlite3SrcListDelete(pParse->db, pName);
@ -2907,7 +2943,8 @@ public:
if (pValue->op == TK_SELECT)
{
QcAliases aliases;
update_field_infos_from_select(aliases, pValue->x.pSelect, NULL);
uint32_t context = 0;
update_field_infos_from_select(aliases, context, pValue->x.pSelect, NULL);
}
}
break;

View File

@ -876,6 +876,7 @@ public:
: m_database(info.database ? info.database : "")
, m_table(info.table ? info.table : "")
, m_column(info.column ? info.column : "")
, m_context(info.context)
{
}
@ -939,12 +940,33 @@ public:
}
out << m_column;
if (m_context != 0)
{
out << "(";
bool first = true;
if (m_context & QC_FIELD_UNION)
{
out << (first ? "" : ", ") << "UNION";
first = false;
}
if (m_context & QC_FIELD_SUBQUERY)
{
out << (first ? "" : ", ") << "SUBQUERY";
first = false;
}
out << ")";
}
}
private:
std::string m_database;
std::string m_table;
std::string m_column;
uint32_t m_context;
};
ostream& operator<<(ostream& out, const QcFieldInfo& x)
@ -1010,7 +1032,17 @@ bool compare_get_field_info(QUERY_CLASSIFIER* pClassifier1,
if (f1 == f2)
{
ss << "Ok : ";
ss << f1;
// TODO: Currently qc_sqlite provides context information, while qc_mysqlembedded
// TODO: does not. To ensure that the output always contains the maximum amount
// TODO: of information, we simply generate both output and print the longest.
stringstream ss1;
ss1 << f1;
stringstream ss2;
ss2 << f2;
ss << (ss1.str().length() > ss2.str().length() ? ss1.str() : ss2.str());
success = true;
}
else