Member Junction
    Preparing search index...
    • Finds which of the supplied identifier names are referenced anywhere in a SQL fragment.

      Built for field-level security, where an ExtraFilter or OrderBy naming a field the user cannot read must be rejected: output stripping alone is security theater, because ExtraFilter: "Salary > 200000" reconstructs the values from the returned row set without the column ever appearing in a result.

      String literals are NOT stripped, [bracketed] / "quoted" / `backticked` identifiers match the same as bare ones, and a match inside a comment still counts. A false positive (a string literal that happens to contain a restricted field's name) rejects a legitimate query and is recoverable — the caller sees an error and rewrites. A false negative silently leaks the data the whole feature exists to protect. For a security gate that trade is not close.

      We search for each supplied NAME inside the fragment; we do NOT tokenize the fragment and look tokens up. Tokenizing forces an identifier character class ([A-Z_][A-Z0-9_]*), and every field name outside that class then becomes unmatchable and silently permitted — Base Salary, Salary%, and Salário all sailed through an earlier tokenizing version. Column names come from the database, so none of those shapes can be assumed away.

      Not an oversight, and not a barrier to adding Oracle or any other dialect. Each way dialects differ here is either already handled or provably safe:

      • Quoting delimiters — SQL Server [x], PostgreSQL/Oracle "x", MySQL `x`. Handled for all of them without knowing which: delimiters are not word characters, so the lookarounds below treat a quoted reference exactly like a bare one.
      • Case folding — PostgreSQL folds unquoted identifiers to lower, Oracle to UPPER, SQL Server compares per collation. Matching case-insensitively is a strict SUPERSET of every one of those rules, so it can never MISS a reference any dialect would resolve. Dialect awareness could only make this narrower (fewer false positives), never safer.
      • Comment and string-literal syntax — irrelevant, because neither is stripped.

      A dialect enum parameter would in fact be the thing that obstructs a new dialect: it forces every caller to thread a value through and forces this file to be revisited per dialect, in exchange for behavior that is already correct. If dialect-specific identifier handling is ever genuinely needed, the seam belongs on the SQLDialect driver (which already exposes PlatformKey / QuoteIdentifier) and the caller — ProviderBase already knows its own platform — passes the normalized form in. That evolution needs no change here.

      KNOWN EDGE, recorded rather than hidden: a field name containing a quoting delimiter (a column literally named Sal]ary or Sal"ary) is escaped by doubling inside a quoted reference ([Sal]]ary], "Sal""ary"), which this literal search would not match. That IS dialect- specific and would need the driver seam above. It requires a column name containing a bracket or double quote, which CodeGen-generated MJ entities do not produce.

      Parameters

      • expression: string

        The SQL fragment to scan (a WHERE predicate, ORDER BY clause, etc.)

      • identifiers: Iterable<string>

        Candidate identifier names, matched case-insensitively

      Returns string[]

      The subset of identifiers referenced, in their original casing, deduplicated. Empty when expression is blank or nothing matches.