t8y2/dbx · info

valid ES SQL pagination regex

Error message

valid ES SQL pagination regex

What it means

Panic from Regex::new(...).expect when compiling the hard-coded ES SQL pagination pattern `(?i)^(.*?)\s+limit\s+(\d+)(?:\s+offset\s+(\d+))?\s*$`. The pattern is a compile-time constant known to be valid, so the expect documents a build-time invariant; a panic means the constant itself was corrupted in an edit. Users cannot trigger it via query input since the regex is never built from user data.

Source

Thrown at crates/dbx-core/src/db/elasticsearch_driver.rs:2468

) -> Result<QueryResult, String> {
    let search = EsIndexedSearch {
        index: translated.index,
        body: translated.body,
        from_plan_pagination: translated.from_plan_pagination,
        // 用户自己写了 LIMIT 时不覆盖行数,否则会把「取 10 条」显示成索引总量。
        report_index_total: !translated.user_limited,
    };
    execute_indexed_search(client, search, start, sql_response_parser, cursor).await
}

/// Split a trailing `LIMIT n OFFSET m` from an ES SQL statement. The OFFSET
/// form is produced by the DBX pagination plan; it must be removed before
/// sending the query to `_sql` so ES SQL cursor pagination can drive paging.
/// A bare user `LIMIT n` (no OFFSET) is preserved as an explicit row cap.
fn es_sql_pagination(query: &str) -> (String, Option<usize>) {
    let trimmed = query.trim().trim_end_matches(';').trim();
    let re =
        Regex::new(r"(?i)^(.*?)\s+limit\s+(\d+)(?:\s+offset\s+(\d+))?\s*$").expect("valid ES SQL pagination regex");
    if let Some(caps) = re.captures(trimmed) {
        let limit = caps.get(2).and_then(|value| value.as_str().parse::<usize>().ok());
        let offset = caps.get(3).and_then(|value| value.as_str().parse::<usize>().ok());
        // Only the plan's `OFFSET 0` first page is safe to strip. A
        // user-written `OFFSET > 0` must keep its explicit offset semantics.
        if offset == Some(0) {
            let base = caps.get(1).map(|value| value.as_str().trim().to_string()).unwrap_or_default();
            (base, limit)
        } else {
            (trimmed.to_string(), limit)
        }
    } else {
        (trimmed.to_string(), None)
    }
}

async fn execute_sql_query(
    client: &EsClient,

View on GitHub (pinned to c0390bff16)

Solutions

  1. Keep the pattern as a validated constant; never interpolate user input into it
  2. Use once_cell::sync::Lazy<Regex> (or std OnceLock) to compile once and fail fast at first use with a clear message
  3. If the pattern ever becomes dynamic, replace expect with match Regex::new(...) returning a proper error
  4. Add a unit test that es_sql_pagination parses sample LIMIT/OFFSET queries to catch accidental corruption

Example fix

// before
let re =
    Regex::new(r"(?i)^(.*?)\s+limit\s+(\d+)(?:\s+offset\s+(\d+))?\s*$").expect("valid ES SQL pagination regex");
// after
static ES_SQL_PAGINATION: Lazy<Regex> = Lazy::new(|| {
    Regex::new(r"(?i)^(.*?)\s+limit\s+(\d+)(?:\s+offset\s+(\d+))?\s*$")
        .expect("valid ES SQL pagination regex")
});
let re = &*ES_SQL_PAGINATION;
Defensive patterns

Strategy: try-catch

Validate before calling

let q = query.trim().trim_end_matches(';');
let has_limit = q.to_lowercase().contains(" limit ");
// no caller-side action needed; the regex is a library constant
let _ = has_limit;

Try / catch

let (sql, limit) = es_sql_pagination(query); // panics only on corrupted constant; catch_unwind is unnecessary — pin with a unit test
assert!(!sql.to_lowercase().contains("offset 0"));

Prevention

When it happens

Trigger: Only fires if the literal regex string is edited into an invalid pattern (typo'd group, unbalanced paren), or if the code is changed to interpolate user/runtime input into the pattern.

Common situations: Hand-edits to the regex literal; refactors parameterizing the pattern with dynamic content; not reachable from es_sql() query input.

Related errors


AI-assisted analysis of t8y2/dbx@c0390bff16 (2026-09-05). Data as JSON: /api/errors/e225f50fe93440d8. Report an issue: GitHub.