代码作者:Qwen3-Coder大模型。
cpp
#include <QRegularExpression>
namespace Database
{
const QStringList nonSelectStarters
{
"insert", "update", "delete", "create", "alter", "drop",
"truncate", "merge", "call", "exec", "begin", "commit",
"rollback", "set", "use", "grant", "revoke", "show", "describe"
};
// 预编译所有正则表达式
static const QRegularExpression multiLineCommentRegex(
R"(/\*.*?\*/)",
QRegularExpression::DotMatchesEverythingOption
);
static const QRegularExpression explainSelectRegex(R"(\bexplain\s+select\b)");
static const QRegularExpression selectRegex(R"(\bselect\b)");
// 预编译带空格的starter
static const QStringList nonSelectStartersWithSpace = []() {
QStringList result;
result.reserve(nonSelectStarters.size());
for (const QString &starter : nonSelectStarters) {
result << starter + " ";
}
return result;
}();
}
static QString removeSqlComments(const QString &sql)
{
if (sql.isEmpty()) {
return sql;
}
QString result;
result.reserve(sql.size()); // 预分配空间
// 单次遍历处理单行注释和多行注释
const int length = sql.length();
int pos = 0;
while (pos < length) {
// 检查单行注释
if (pos + 1 < length && sql[pos] == '-' && sql[pos + 1] == '-') {
// 跳过直到行尾
while (pos < length && sql[pos] != '\n') {
++pos;
}
if (pos < length && sql[pos] == '\n') {
result += '\n'; // 保留换行符
++pos;
}
continue;
}
// 检查多行注释
if (pos + 1 < length && sql[pos] == '/' && sql[pos + 1] == '*') {
// 跳过直到注释结束
pos += 2; // 跳过 "/*"
while (pos + 1 < length && !(sql[pos] == '*' && sql[pos + 1] == '/')) {
++pos;
}
if (pos + 1 < length) {
pos += 2; // 跳过 "*/"
}
result += ' '; // 用空格替换注释
continue;
}
// 普通字符
if (pos < length) {
result += sql[pos];
++pos;
}
}
return result.trimmed();
}
bool isSelectQuery(const QString &sql)
{
QString trimmedSql = sql.trimmed();
if (trimmedSql.isEmpty()) {
return false;
}
// 移除SQL注释
QString cleanSql = removeSqlComments(trimmedSql);
QString lowerSql = cleanSql.toLower();
// 检查是否以非SELECT语句开头
for (const QString &starter : Database::nonSelectStartersWithSpace) {
if (lowerSql.startsWith(starter)) {
return false;
}
}
// EXPLAIN特殊处理
if (lowerSql.startsWith("explain ")) {
if (Database::explainSelectRegex.match(lowerSql).hasMatch()) {
return false;
}
}
// 查找第一个SELECT的位置
QRegularExpressionMatch match = Database::selectRegex.match(lowerSql);
if (match.hasMatch()) {
int selectPos = match.capturedStart();
QString beforeSelect = lowerSql.left(selectPos).trimmed();
// 检查SELECT之后的内容
QString afterSelect = lowerSql.mid(match.capturedEnd()).trimmed();
// 如果SELECT后面没有任何有意义的内容,不算有效查询
if (afterSelect.isEmpty() ||
afterSelect.startsWith(";") ||
afterSelect.startsWith(")") || // 在子查询中
afterSelect.startsWith(",")) { // 在列表中
return false;
}
// 检查SELECT前面是否只有WITH(CTE)
if (!beforeSelect.isEmpty() && !beforeSelect.startsWith("with ")) {
return false;
}
return true;
}
return false;
}
void testIsSelectQuery() {
struct TestCase {
QString sql;
bool expected;
QString description;
};
QList<TestCase> testCases = {
// 正确的SELECT语句
{"SELECT * FROM users;", true, "基本SELECT语句"},
{"select * from users;", true, "小写SELECT语句"},
{" SELECT * FROM users ;", true, "带多余空格的SELECT"},
{"SELECT id, name FROM users WHERE age > 18;", true, "带WHERE的SELECT"},
{"SELECT COUNT(*) FROM users GROUP BY department;", true, "带GROUP BY的SELECT"},
{"SELECT u.name, p.title FROM users u JOIN posts p ON u.id = p.user_id;", true, "JOIN查询"},
{"WITH temp AS (SELECT * FROM users) SELECT * FROM temp;", true, "WITH子句查询"},
{"WITH cte AS (SELECT id FROM users) SELECT * FROM cte WHERE id > 10;", true, "复杂CTE查询"},
// EXPLAIN相关(根据需求可能需要调整)
{"EXPLAIN SELECT * FROM users;", false, "EXPLAIN语句(可能不算纯SELECT)"},
{"EXPLAIN ANALYZE SELECT * FROM users;", false, "EXPLAIN ANALYZE语句"},
// 非SELECT语句
{"INSERT INTO users (name) VALUES ('John');", false, "INSERT语句"},
{"UPDATE users SET name = 'Jane' WHERE id = 1;", false, "UPDATE语句"},
{"DELETE FROM users WHERE id = 1;", false, "DELETE语句"},
{"CREATE TABLE users (id INT, name VARCHAR(50));", false, "CREATE语句"},
{"ALTER TABLE users ADD COLUMN email VARCHAR(100);", false, "ALTER语句"},
{"DROP TABLE users;", false, "DROP语句"},
{"CREATE INDEX idx_name ON users(name);", false, "CREATE INDEX语句"},
{"TRUNCATE TABLE users;", false, "TRUNCATE语句"},
{"MERGE INTO target USING source ON condition;", false, "MERGE语句"},
{"CALL stored_procedure();", false, "CALL语句"},
// 边界情况
{"", false, "空字符串"},
{" ", false, "只有空格"},
{"SELECT", false, "只有SELECT关键字"},
{"SELECT;", false, "SELECT后直接分号"},
{"SELECT\n*\nFROM\nusers;", true, "带换行符的SELECT"},
{"SELECT\t*\tFROM\tusers;", true, "带制表符的SELECT"},
{"-- SELECT * FROM users; \n SELECT * FROM posts;", true, "注释后有SELECT"},
{"/* SELECT comment */ SELECT * FROM users;", true, "多行注释后的SELECT"},
{"SELECT123 FROM table;", false, "SELECT作为标识符(不是关键字)"},
{"SELECTED FROM table;", false, "SELECTED不是SELECT"},
// 复杂查询
{"SELECT * FROM (SELECT * FROM users) AS subquery;", true, "子查询"},
{"SELECT * FROM users UNION SELECT * FROM admins;", true, "UNION查询"},
{"SELECT CASE WHEN id > 0 THEN 'positive' ELSE 'negative' END FROM numbers;", true, "CASE语句"},
{"SELECT * FROM users ORDER BY name LIMIT 10;", true, "ORDER BY和LIMIT"},
// 混合大小写
{"Select * From Users;", true, "混合大小写1"},
{"SeLeCt * FrOm UsErS;", true, "混合大小写2"},
{"SELECT DISTINCT name FROM users;", true, "带DISTINCT的SELECT"},
// 包含SELECT但不是SELECT语句
{"SELECTED_COLUMNS FROM table;", false, "SELECT作为标识符"},
{"SELECT_USER FROM mapping;", false, "SELECT作为标识符"},
};
qDebug() << "=== 测试 SELECT 查询检测函数 ===";
int passed = 0;
int total = testCases.size();
for (int i = 0; i < testCases.size(); ++i) {
const auto& testCase = testCases[i];
bool result = isSelectQuery(testCase.sql);
bool success = (result == testCase.expected);
qDebug() << QString("测试 %1: %2").arg(i + 1, 3).arg(
success ? "✓ PASS" : "✗ FAIL"
);
qDebug() << " SQL: " << testCase.sql;
qDebug() << " 期望: " << (testCase.expected ? "true" : "false")
<< ", 实际: " << (result ? "true" : "false");
qDebug() << " 说明: " << testCase.description;
if (success) {
passed++;
} else {
qDebug() << " *** 测试失败 ***";
}
qDebug() << "";
}
qDebug() << QString("测试结果: %1/%2 通过").arg(passed).arg(total);
qDebug() << QString("成功率: %1%").arg((double)passed / total * 100, 0, 'f', 1);
}
int main(int argc, char *argv[])
{
testIsSelectQuery();
return 1;
}