Qt:判断一个sql语句是否是select语句

代码作者: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;
}
相关推荐
武子康3 小时前
Java-169 Neo4j CQL 实战速查:字符串/聚合/关系与多跳查询
java·开发语言·数据库·python·sql·nosql·neo4j
爱吃土豆的马铃薯ㅤㅤㅤㅤㅤㅤㅤㅤㅤ3 小时前
MyBatis Plus中执行原生SQL语句方法
python·sql·mybatis
西游音月4 小时前
(4)框架搭建:Qt实战项目之主窗体介绍
开发语言·qt
百***27115 小时前
UNION 和 UNION ALL 的区别:深入解析 SQL 中的合并操作
数据库·sql·oracle
_OP_CHEN8 小时前
从零开始的Qt开发指南:(三)信号与槽的概念与使用
开发语言·c++·qt·前端开发·qt creator·信号与槽·gui开发
superlls8 小时前
(Mysql)Mysql八股大杂烩
数据库·sql
神仙别闹17 小时前
基于QT(C++) 实现哈夫曼压缩(多线程)
java·c++·qt
小陈又菜18 小时前
【QT学习之路】网络通信新次元!Qt TCP双侠:Server监听瞬息,Socket连接万变
qt·网络协议·tcp/ip·socket
_Minato_18 小时前
数据库知识整理——SQL数据定义
数据库·sql·mysql·oracle·database·数据库开发·数据库架构