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. */
|
||||
} 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
|
||||
* implement and return.
|
||||
@ -150,6 +159,7 @@ typedef struct query_classifier
|
||||
char* (*qc_get_prepare_name)(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_function_info)(GWBUF* stmt, const QC_FUNCTION_INFO** infos, size_t* n_infos);
|
||||
} 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);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
@ -2584,6 +2597,7 @@ MXS_MODULE* MXS_CREATE_MODULE()
|
||||
qc_get_prepare_name,
|
||||
qc_get_prepare_operation,
|
||||
qc_get_field_info,
|
||||
qc_get_function_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
|
||||
*/
|
||||
@ -3207,6 +3237,7 @@ MXS_MODULE* MXS_CREATE_MODULE()
|
||||
qc_sqlite_get_prepare_name,
|
||||
qc_sqlite_get_prepare_operation,
|
||||
qc_sqlite_get_field_info,
|
||||
qc_sqlite_get_function_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)
|
||||
{
|
||||
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_operation(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(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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
QC_TRACE();
|
||||
|
Reference in New Issue
Block a user