qc: Reveal function usage
MXS-1070 Just interface changes. Implementation follow in separate changes.
This commit is contained in:
@ -118,6 +118,15 @@ typedef struct qc_field_info
|
|||||||
uint32_t usage; /** Bitfield denoting where the column appears. */
|
uint32_t usage; /** Bitfield denoting where the column appears. */
|
||||||
} QC_FIELD_INFO;
|
} QC_FIELD_INFO;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* QC_FUNCTION_INFO contains information about a function used in a statement.
|
||||||
|
*/
|
||||||
|
typedef struct qc_function_info
|
||||||
|
{
|
||||||
|
char* name; /** Name of function. */
|
||||||
|
uint32_t usage; /** Bitfield denoting where the column appears. */
|
||||||
|
} QC_FUNCTION_INFO;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* QUERY_CLASSIFIER defines the object a query classifier plugin must
|
* QUERY_CLASSIFIER defines the object a query classifier plugin must
|
||||||
* implement and return.
|
* implement and return.
|
||||||
@ -150,6 +159,7 @@ typedef struct query_classifier
|
|||||||
char* (*qc_get_prepare_name)(GWBUF* stmt);
|
char* (*qc_get_prepare_name)(GWBUF* stmt);
|
||||||
qc_query_op_t (*qc_get_prepare_operation)(GWBUF* stmt);
|
qc_query_op_t (*qc_get_prepare_operation)(GWBUF* stmt);
|
||||||
void (*qc_get_field_info)(GWBUF* stmt, const QC_FIELD_INFO** infos, size_t* n_infos);
|
void (*qc_get_field_info)(GWBUF* stmt, const QC_FIELD_INFO** infos, size_t* n_infos);
|
||||||
|
void (*qc_get_function_info)(GWBUF* stmt, const QC_FUNCTION_INFO** infos, size_t* n_infos);
|
||||||
} QUERY_CLASSIFIER;
|
} QUERY_CLASSIFIER;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -297,6 +307,21 @@ char* qc_field_usage_mask_to_string(uint32_t usage_mask);
|
|||||||
*/
|
*/
|
||||||
void qc_get_field_info(GWBUF* stmt, const QC_FIELD_INFO** infos, size_t* n_infos);
|
void qc_get_field_info(GWBUF* stmt, const QC_FIELD_INFO** infos, size_t* n_infos);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns information about function usage.
|
||||||
|
*
|
||||||
|
* @param stmt A buffer containing a COM_QUERY packet.
|
||||||
|
* @param infos Pointer to pointer that after the call will point to an
|
||||||
|
* array of QC_FUNCTION_INFO:s.
|
||||||
|
* @param n_infos Pointer to size_t variable where the number of items
|
||||||
|
* in @c infos will be returned.
|
||||||
|
*
|
||||||
|
* @note The returned array belongs to the GWBUF and remains valid for as
|
||||||
|
* long as the GWBUF is valid. If the data is needed for longer than
|
||||||
|
* that, it must be copied.
|
||||||
|
*/
|
||||||
|
void qc_get_function_info(GWBUF* stmt, const QC_FUNCTION_INFO** infos, size_t* n_infos);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the statement, with literals replaced with question marks.
|
* Returns the statement, with literals replaced with question marks.
|
||||||
*
|
*
|
||||||
|
@ -2432,6 +2432,19 @@ void qc_get_field_info(GWBUF* buf, const QC_FIELD_INFO** infos, size_t* n_infos)
|
|||||||
*n_infos = pi->field_infos_len;
|
*n_infos = pi->field_infos_len;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void qc_get_function_info(GWBUF* buf, const QC_FUNCTION_INFO** infos, size_t* n_infos)
|
||||||
|
{
|
||||||
|
*infos = NULL;
|
||||||
|
*n_infos = 0;
|
||||||
|
|
||||||
|
if (!ensure_query_is_parsed(buf))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Implement functionality.
|
||||||
|
}
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -2584,6 +2597,7 @@ MXS_MODULE* MXS_CREATE_MODULE()
|
|||||||
qc_get_prepare_name,
|
qc_get_prepare_name,
|
||||||
qc_get_prepare_operation,
|
qc_get_prepare_operation,
|
||||||
qc_get_field_info,
|
qc_get_field_info,
|
||||||
|
qc_get_function_info
|
||||||
};
|
};
|
||||||
|
|
||||||
static MXS_MODULE info =
|
static MXS_MODULE info =
|
||||||
|
@ -3181,6 +3181,36 @@ void qc_sqlite_get_field_info(GWBUF* query, const QC_FIELD_INFO** infos, size_t*
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void qc_sqlite_get_function_info(GWBUF* query, const QC_FUNCTION_INFO** infos, size_t* n_infos)
|
||||||
|
{
|
||||||
|
QC_TRACE();
|
||||||
|
ss_dassert(this_unit.initialized);
|
||||||
|
ss_dassert(this_thread.initialized);
|
||||||
|
|
||||||
|
*infos = NULL;
|
||||||
|
*n_infos = 0;
|
||||||
|
|
||||||
|
QC_SQLITE_INFO* info = get_query_info(query);
|
||||||
|
|
||||||
|
if (info)
|
||||||
|
{
|
||||||
|
if (qc_info_is_valid(info->status))
|
||||||
|
{
|
||||||
|
// TODO: Implement functionality.
|
||||||
|
*infos = NULL;
|
||||||
|
*n_infos = 0;
|
||||||
|
}
|
||||||
|
else if (MXS_LOG_PRIORITY_IS_ENABLED(LOG_INFO))
|
||||||
|
{
|
||||||
|
log_invalid_data(query, "cannot report field info");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
MXS_ERROR("The query could not be parsed. Response not valid.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* EXPORTS
|
* EXPORTS
|
||||||
*/
|
*/
|
||||||
@ -3207,6 +3237,7 @@ MXS_MODULE* MXS_CREATE_MODULE()
|
|||||||
qc_sqlite_get_prepare_name,
|
qc_sqlite_get_prepare_name,
|
||||||
qc_sqlite_get_prepare_operation,
|
qc_sqlite_get_prepare_operation,
|
||||||
qc_sqlite_get_field_info,
|
qc_sqlite_get_field_info,
|
||||||
|
qc_sqlite_get_function_info,
|
||||||
};
|
};
|
||||||
|
|
||||||
static MXS_MODULE info =
|
static MXS_MODULE info =
|
||||||
|
@ -1060,6 +1060,132 @@ bool compare_get_field_info(QUERY_CLASSIFIER* pClassifier1, GWBUF* pCopy1,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class QcFunctionInfo
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
QcFunctionInfo(const QC_FUNCTION_INFO& info)
|
||||||
|
: m_name(info.name)
|
||||||
|
, m_usage(info.usage)
|
||||||
|
{}
|
||||||
|
|
||||||
|
bool eq(const QcFunctionInfo& rhs) const
|
||||||
|
{
|
||||||
|
return
|
||||||
|
m_name == rhs.m_name &&
|
||||||
|
m_usage == rhs.m_usage;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool lt(const QcFunctionInfo& rhs) const
|
||||||
|
{
|
||||||
|
bool rv = false;
|
||||||
|
|
||||||
|
if (m_name < rhs.m_name)
|
||||||
|
{
|
||||||
|
rv = true;
|
||||||
|
}
|
||||||
|
else if (m_name > rhs.m_name)
|
||||||
|
{
|
||||||
|
rv = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rv = (m_usage < rhs.m_usage);
|
||||||
|
}
|
||||||
|
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
void print(ostream& out) const
|
||||||
|
{
|
||||||
|
out << m_name;
|
||||||
|
|
||||||
|
out << "(";
|
||||||
|
char* s = qc_field_usage_mask_to_string(m_usage);
|
||||||
|
out << s;
|
||||||
|
free(s);
|
||||||
|
out << ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string m_name;
|
||||||
|
uint32_t m_usage;
|
||||||
|
};
|
||||||
|
|
||||||
|
ostream& operator << (ostream& out, const QcFunctionInfo& x)
|
||||||
|
{
|
||||||
|
x.print(out);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
ostream& operator << (ostream& out, std::set<QcFunctionInfo>& x)
|
||||||
|
{
|
||||||
|
std::set<QcFunctionInfo>::iterator i = x.begin();
|
||||||
|
std::set<QcFunctionInfo>::iterator end = x.end();
|
||||||
|
|
||||||
|
while (i != end)
|
||||||
|
{
|
||||||
|
out << *i++;
|
||||||
|
if (i != end)
|
||||||
|
{
|
||||||
|
out << " ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator < (const QcFunctionInfo& lhs, const QcFunctionInfo& rhs)
|
||||||
|
{
|
||||||
|
return lhs.lt(rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator == (const QcFunctionInfo& lhs, const QcFunctionInfo& rhs)
|
||||||
|
{
|
||||||
|
return lhs.eq(rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool compare_get_function_info(QUERY_CLASSIFIER* pClassifier1, GWBUF* pCopy1,
|
||||||
|
QUERY_CLASSIFIER* pClassifier2, GWBUF* pCopy2)
|
||||||
|
{
|
||||||
|
bool success = false;
|
||||||
|
const char HEADING[] = "qc_get_function_info : ";
|
||||||
|
|
||||||
|
const QC_FUNCTION_INFO* infos1;
|
||||||
|
const QC_FUNCTION_INFO* infos2;
|
||||||
|
size_t n_infos1;
|
||||||
|
size_t n_infos2;
|
||||||
|
|
||||||
|
pClassifier1->qc_get_function_info(pCopy1, &infos1, &n_infos1);
|
||||||
|
pClassifier2->qc_get_function_info(pCopy2, &infos2, &n_infos2);
|
||||||
|
|
||||||
|
stringstream ss;
|
||||||
|
ss << HEADING;
|
||||||
|
|
||||||
|
int i;
|
||||||
|
|
||||||
|
std::set<QcFunctionInfo> f1;
|
||||||
|
f1.insert(infos1, infos1 + n_infos1);
|
||||||
|
|
||||||
|
std::set<QcFunctionInfo> f2;
|
||||||
|
f2.insert(infos2, infos2 + n_infos2);
|
||||||
|
|
||||||
|
if (f1 == f2)
|
||||||
|
{
|
||||||
|
ss << "Ok : ";
|
||||||
|
ss << f1;
|
||||||
|
success = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ss << "ERR: " << f1 << " != " << f2;
|
||||||
|
}
|
||||||
|
|
||||||
|
report(success, ss.str());
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
bool compare(QUERY_CLASSIFIER* pClassifier1, QUERY_CLASSIFIER* pClassifier2, const string& s)
|
bool compare(QUERY_CLASSIFIER* pClassifier1, QUERY_CLASSIFIER* pClassifier2, const string& s)
|
||||||
{
|
{
|
||||||
GWBUF* pCopy1 = create_gwbuf(s);
|
GWBUF* pCopy1 = create_gwbuf(s);
|
||||||
@ -1080,6 +1206,7 @@ bool compare(QUERY_CLASSIFIER* pClassifier1, QUERY_CLASSIFIER* pClassifier2, con
|
|||||||
errors += !compare_get_prepare_name(pClassifier1, pCopy1, pClassifier2, pCopy2);
|
errors += !compare_get_prepare_name(pClassifier1, pCopy1, pClassifier2, pCopy2);
|
||||||
errors += !compare_get_prepare_operation(pClassifier1, pCopy1, pClassifier2, pCopy2);
|
errors += !compare_get_prepare_operation(pClassifier1, pCopy1, pClassifier2, pCopy2);
|
||||||
errors += !compare_get_field_info(pClassifier1, pCopy1, pClassifier2, pCopy2);
|
errors += !compare_get_field_info(pClassifier1, pCopy1, pClassifier2, pCopy2);
|
||||||
|
errors += !compare_get_function_info(pClassifier1, pCopy1, pClassifier2, pCopy2);
|
||||||
|
|
||||||
gwbuf_free(pCopy1);
|
gwbuf_free(pCopy1);
|
||||||
gwbuf_free(pCopy2);
|
gwbuf_free(pCopy2);
|
||||||
|
@ -217,6 +217,14 @@ void qc_get_field_info(GWBUF* query, const QC_FIELD_INFO** infos, size_t* n_info
|
|||||||
classifier->qc_get_field_info(query, infos, n_infos);
|
classifier->qc_get_field_info(query, infos, n_infos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void qc_get_function_info(GWBUF* query, const QC_FUNCTION_INFO** infos, size_t* n_infos)
|
||||||
|
{
|
||||||
|
QC_TRACE();
|
||||||
|
ss_dassert(classifier);
|
||||||
|
|
||||||
|
classifier->qc_get_function_info(query, infos, n_infos);
|
||||||
|
}
|
||||||
|
|
||||||
char** qc_get_database_names(GWBUF* query, int* sizep)
|
char** qc_get_database_names(GWBUF* query, int* sizep)
|
||||||
{
|
{
|
||||||
QC_TRACE();
|
QC_TRACE();
|
||||||
|
Reference in New Issue
Block a user