0

私はいくつかの SQL where 句の解析を行っており、.NET API を使用している「Rad Software Regular Expression Desginer」を使用して、文字列リテラルの外側の列を見つけるために動作する RegEx を設計しました。設計された RegEx が Java でも動作することを確認するために、もちろん API (1.5 および 1.6) を使用してテストしました。しかし、それはうまくいきません。メッセージを受け取りました

「後読みグループには、インデックス 28 付近に明らかな最大長がありません」。

解析しようとしている文字列は

Column_1='test''the''stuff''all''day''long' AND Column_2='000' AND  TheVeryColumnIWantToFind      =    'Column_1=''test''''the''''stuff''''all''''day''''long'' AND Column_2=''000'' AND  TheVeryColumnIWantToFind   =    ''   TheVeryColumnIWantToFind   =    '' AND (Column_3 is null or Column_3 = ''Not interesting'') AND ''1'' = ''1''' AND (Column_3 is null or Column_3 = 'Still not interesting') AND '1' = '1'

ご想像のとおり、より複雑な SQL where 句で RegEx が失敗しないように、ある種の最悪のケースを作成しようとしました。

正規表現自体は次のようになります

(?i:(?<!=\s*'(?:[^']|(?:''))*)((?<=\s*)TheVeryColumnIWantToFind(?=(?:\s+|=))))

もっと洗練された RegEx があるかどうかはわかりませんが (おそらく存在するでしょう)、それはトリックを実行するので、現時点では重要ではありません。

RegEx を簡単に説明すると、探している列が見つかった場合、列名が文字列リテラルで使用されているかどうかを判断するために、否定的な後読みが行われます。もしそうなら、それは一致しません。そうでない場合は一致します。

質問に戻ります。前述したように、Java では動作しません。何が機能し、私が望む結果になりますか?
私は、Java が無制限の後読みをサポートしていないように見えることを知りましたが、それでも動作させることができませんでした。
後読みが常に検索オフセットから現在の検索位置までに制限をかけているのは正しくありませんか? それで、「位置 - オフセット」のような結果になりますか?

4

1 に答える 1

0

私はついに解決策を見つけました。ここで質問したので、もちろんあなたと共有します。

private static final String SQL_STRING_LITERALS_REGEX = "'(?:(?:[^']|(?:''))*)'";
private static final char DOT = '.';

private ArrayList<int[]> getNonStringLiteralRegions(String exclusion) {
    ArrayList<int[]> regions = new ArrayList<int[]>();

    int lastEnd = 0;
    Matcher m = Pattern.compile(SQL_STRING_LITERALS_REGEX).matcher(exclusion);
    while (m.find()) {
        regions.add(new int[] {lastEnd, m.start()});
        lastEnd = m.end();
    }
    if (lastEnd < exclusion.length())
        // We didn't cover the last part of the exclusion yet.
        regions.add(new int[] {lastEnd, exclusion.length()});

    return regions;
}

protected final String getFixedExclusion(String exclusion, String[] columns, String alias) {
    if (alias == null)
        throw new NullPointerException("Alias must not be null.");
    else if (alias.charAt(alias.length() - 1) != DOT)
        alias += DOT;

    StringBuilder b = new StringBuilder(exclusion);
    ArrayList<int[]> regions = getNonStringLiteralRegions(exclusion);
    for (int i = regions.size() - 1; i >= 0; --i) {
        // Reverse iteration to keep valid indices for the lower regions.
        int start = regions.get(i)[0], end = regions.get(i)[1];
        String s = exclusion.substring(start, end);
        for (String column : columns)
            s = s.replaceAll("(?<=^|[\\W&&\\D])(?i:" + column + ")(?=[\\W&&\\D]|$)", alias + column);
        b.replace(start, end, s);
    }

    return b.toString();
}

今回の秘訣は、列を「Alias.ColumnName」に置き換えるときに、単純に SQL 文字列リテラルを見つけてそれらを避けることです。置換するときは、列名全体を確認することが重要です。したがって、where 句の列 "Column_1" を置き換える場合

WHERE Column_1 = Column_2 AND Column_11 = Column_22

「Column_11」はそのままにしておきます。(これを心に留めておくことが重要だと思います。そのため、同様の問題に直面している人のためにここで言及します。)
それでも、これは回避策にすぎないと思います。このロジックの必要性を回避できる場合は、それが最善です。そうするために。

わかりました、とにかく助けてくれてありがとう。今後の質問があれば喜んでお答えします。

于 2010-04-28T12:39:03.683 に答える