yeg
6 天以前 e38aca8134570513435c003c25dc05f1ae51c9c9
battery-device-neware/src/main/java/com/battery/device/neware/ReadOnlyVendorDataSource.java
@@ -33,6 +33,12 @@
public final class ReadOnlyVendorDataSource implements DataSource
{
    private static final List<String> ALLOWED_PREFIXES = List.of("select", "show", "describe", "desc");
    /**
     * 加锁读会真的阻塞厂商写入,所以它虽然以 {@code select} 开头也要单独拒绝:
     * 现场实测厂商库 {@code tx_isolation=REPEATABLE-READ},行锁会挡住上位机的写库。
     */
    private static final List<String> LOCKING_CLAUSES = List.of("for update", "for share",
            "lock in share mode", "updlock", "holdlock", "xlock", "tablock");
    private final DataSource delegate;
    private final String sourceLabel;
@@ -117,13 +123,17 @@
    private void requireQuery(String sql)
    {
        if (isQuery(sql))
        boolean query = isQuery(sql);
        boolean locking = query && containsLockingClause(sql);
        if (query && !locking)
        {
            audit.recordForwarded(sql);
            return;
        }
        audit.recordRejected(sql);
        throw new NonQueryStatementException("厂商数据源只允许查询语句:" + abbreviate(sql));
        throw new NonQueryStatementException(locking
                ? "厂商数据源禁止加锁读(会阻塞厂商写入):" + abbreviate(sql)
                : "厂商数据源只允许查询语句:" + abbreviate(sql));
    }
    /** 只按语句首关键字判定,避免用正则近似 SQL 语法。 */
@@ -144,6 +154,21 @@
        return false;
    }
    /**
     * 语句里是否带加锁读子句。这是**语法层的拦截**,不是 SQL 解析器:厂商取数的语句由本模块固定给出,
     * 不会把加锁子句藏在字符串常量里;新增取数语句时由 {@code ReadOnlyVendorDataSourceTest} 一起覆盖。
     */
    static boolean containsLockingClause(String sql)
    {
        if (sql == null) return false;
        String normalized = stripLeadingComments(sql).toLowerCase(Locale.ROOT).replaceAll("\\s+", " ");
        for (String clause : LOCKING_CLAUSES)
        {
            if (normalized.contains(clause)) return true;
        }
        return false;
    }
    private static String stripLeadingComments(String sql)
    {
        String remaining = sql.trim();