{"record":{"id":"e225f50fe93440d8","repo":"t8y2/dbx","slug":"valid-es-sql-pagination-regex","errorCode":null,"errorMessage":"valid ES SQL pagination regex","messagePattern":"valid ES SQL pagination regex","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"info","filePath":"crates/dbx-core/src/db/elasticsearch_driver.rs","lineNumber":2468,"sourceCode":") -> Result<QueryResult, String> {\n    let search = EsIndexedSearch {\n        index: translated.index,\n        body: translated.body,\n        from_plan_pagination: translated.from_plan_pagination,\n        // 用户自己写了 LIMIT 时不覆盖行数，否则会把「取 10 条」显示成索引总量。\n        report_index_total: !translated.user_limited,\n    };\n    execute_indexed_search(client, search, start, sql_response_parser, cursor).await\n}\n\n/// Split a trailing `LIMIT n OFFSET m` from an ES SQL statement. The OFFSET\n/// form is produced by the DBX pagination plan; it must be removed before\n/// sending the query to `_sql` so ES SQL cursor pagination can drive paging.\n/// A bare user `LIMIT n` (no OFFSET) is preserved as an explicit row cap.\nfn es_sql_pagination(query: &str) -> (String, Option<usize>) {\n    let trimmed = query.trim().trim_end_matches(';').trim();\n    let re =\n        Regex::new(r\"(?i)^(.*?)\\s+limit\\s+(\\d+)(?:\\s+offset\\s+(\\d+))?\\s*$\").expect(\"valid ES SQL pagination regex\");\n    if let Some(caps) = re.captures(trimmed) {\n        let limit = caps.get(2).and_then(|value| value.as_str().parse::<usize>().ok());\n        let offset = caps.get(3).and_then(|value| value.as_str().parse::<usize>().ok());\n        // Only the plan's `OFFSET 0` first page is safe to strip. A\n        // user-written `OFFSET > 0` must keep its explicit offset semantics.\n        if offset == Some(0) {\n            let base = caps.get(1).map(|value| value.as_str().trim().to_string()).unwrap_or_default();\n            (base, limit)\n        } else {\n            (trimmed.to_string(), limit)\n        }\n    } else {\n        (trimmed.to_string(), None)\n    }\n}\n\nasync fn execute_sql_query(\n    client: &EsClient,","sourceCodeStart":2450,"sourceCodeEnd":2486,"githubUrl":"https://github.com/t8y2/dbx/blob/c0390bff16418b651f4728520d99adf8ce48829a/crates/dbx-core/src/db/elasticsearch_driver.rs#L2450-L2486","documentation":"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.","triggerScenarios":"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.","commonSituations":"Hand-edits to the regex literal; refactors parameterizing the pattern with dynamic content; not reachable from es_sql() query input.","solutions":["Keep the pattern as a validated constant; never interpolate user input into it","Use once_cell::sync::Lazy<Regex> (or std OnceLock) to compile once and fail fast at first use with a clear message","If the pattern ever becomes dynamic, replace expect with match Regex::new(...) returning a proper error","Add a unit test that es_sql_pagination parses sample LIMIT/OFFSET queries to catch accidental corruption"],"exampleFix":"// before\nlet re =\n    Regex::new(r\"(?i)^(.*?)\\s+limit\\s+(\\d+)(?:\\s+offset\\s+(\\d+))?\\s*$\").expect(\"valid ES SQL pagination regex\");\n// after\nstatic ES_SQL_PAGINATION: Lazy<Regex> = Lazy::new(|| {\n    Regex::new(r\"(?i)^(.*?)\\s+limit\\s+(\\d+)(?:\\s+offset\\s+(\\d+))?\\s*$\")\n        .expect(\"valid ES SQL pagination regex\")\n});\nlet re = &*ES_SQL_PAGINATION;","handlingStrategy":"try-catch","validationCode":"let q = query.trim().trim_end_matches(';');\nlet has_limit = q.to_lowercase().contains(\" limit \");\n// no caller-side action needed; the regex is a library constant\nlet _ = has_limit;","typeGuard":null,"tryCatchPattern":"let (sql, limit) = es_sql_pagination(query); // panics only on corrupted constant; catch_unwind is unnecessary — pin with a unit test\nassert!(!sql.to_lowercase().contains(\"offset 0\"));","preventionTips":["Never interpolate user input into regex patterns","Compile fixed patterns once with once_cell Lazy","Add unit tests covering LIMIT-only, LIMIT OFFSET 0, and LIMIT OFFSET n queries"],"tags":["rust","elasticsearch","regex","panic","sql"],"backgroundTag":"invalid-regex-pattern","analyzedSha":"c0390bff16418b651f4728520d99adf8ce48829a","analyzedAt":"2026-09-05T23:05:10.900Z","contentChangedAt":"2026-09-05T23:05:10.900Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}