From 7c2f13394a54b1bad37c35fcc2db8fdf5640d21c Mon Sep 17 00:00:00 2001 From: Chris Eubank <108756251+christianeu-db@users.noreply.github.com> Date: Mon, 12 Oct 2026 18:49:34 +0000 Subject: [PATCH] docs(core-spec): escape || so it renders in expression language tables In GitHub-Flavored Markdown an unescaped `|` acts as a table column delimiter even inside a code span, so the `||` concatenation operator in the String Manipulation and Common Dialect Variations tables spawned phantom columns instead of rendering. Escape the pipes as `\|\|`, matching the existing convention in converters/cube/README.md. The thoughtspot converter parses this spec file to assert its catalog covers every construct, and that parser was built around the unescaped `||`. Teach its _clean() helper to undo the `\|` escape so the extracted construct name stays `str1 || str2`; otherwise the escaped source leaks backslashes and test_catalog_covers_the_spec fails. Reported by @Haoranli503. Co-authored-by: Isaac --- .../src/ossie_thoughtspot/expressions/catalog.py | 8 +++++--- core-spec/expression_language.md | 4 ++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/converters/thoughtspot/src/ossie_thoughtspot/expressions/catalog.py b/converters/thoughtspot/src/ossie_thoughtspot/expressions/catalog.py index 321321e0f..1ed33f1b6 100644 --- a/converters/thoughtspot/src/ossie_thoughtspot/expressions/catalog.py +++ b/converters/thoughtspot/src/ossie_thoughtspot/expressions/catalog.py @@ -1729,7 +1729,8 @@ def _split_table_row(line: str) -> list[str]: The document contains at least one cell whose code span itself contains a pipe character - the `||` concatenation operator, written as - `` `str1 || str2` `` - which a naive `str.split("|")` would shred into + `` `str1 \\|\\| str2` `` (GitHub-Flavored Markdown requires escaping pipes + even inside a code span) - which a naive `str.split("|")` would shred into extra empty cells. Backticks otherwise never appear as literal (non-code) content in this document's tables, so "toggle on backtick" is a safe, general rule rather than a special case for that one row. @@ -1756,8 +1757,9 @@ def _split_table_row(line: str) -> list[str]: def _clean(cell: str) -> str: - """Strip markdown code-span backticks, leaving the underlying syntax text.""" - return cell.replace("`", "").strip() + """Strip markdown code-span backticks and unescape pipes, leaving the + underlying syntax text.""" + return cell.replace("`", "").replace("\\|", "|").strip() def _is_section_excluded(heading_stack: dict[int, str]) -> bool: diff --git a/core-spec/expression_language.md b/core-spec/expression_language.md index cc847e53a..92e62251c 100644 --- a/core-spec/expression_language.md +++ b/core-spec/expression_language.md @@ -407,7 +407,7 @@ a dialect extension. | Function | Syntax | Description | | :---- | :---- | :---- | | `CONCAT` | `CONCAT(str1, str2, ...)` | Concatenate strings | -| `||` | `str1 || str2` | Concatenation operator | +| `\|\|` | `str1 \|\| str2` | Concatenation operator | | `LENGTH` | `LENGTH(str)` | String length in characters | | `LOWER` | `LOWER(str)` | Convert to lowercase | | `UPPER` | `UPPER(str)` | Convert to uppercase | @@ -673,7 +673,7 @@ expression: | :---- | :---- | :---- | :---- | :---- | :---- | | Date truncation | `DATE_TRUNC('month', d)` | `DATE_TRUNC('month', d)` | `DATE_TRUNC(d, MONTH)` | `DATE_TRUNC('month', d)` | `DATE_TRUNC('month', d)` | | Date add | `DATEADD(day, 7, d)` | `DATEADD(day, 7, d)` | `DATE_ADD(d, INTERVAL 7 DAY)` | `DATE_ADD(d, 7)` | `d + INTERVAL '7 days'` | -| String concat | `CONCAT(a, b)` | `CONCAT(a, b)` | `CONCAT(a, b)` | `CONCAT(a, b)` | `a || b` | +| String concat | `CONCAT(a, b)` | `CONCAT(a, b)` | `CONCAT(a, b)` | `CONCAT(a, b)` | `a \|\| b` | | Null coalesce | `COALESCE(a, b)` | `COALESCE(a, b)` or `NVL(a, b)` | `COALESCE(a, b)` or `IFNULL(a, b)` | `COALESCE(a, b)` | `COALESCE(a, b)` | | Current timestamp | `CURRENT_TIMESTAMP` | `CURRENT_TIMESTAMP()` | `CURRENT_TIMESTAMP()` | `CURRENT_TIMESTAMP()` | `CURRENT_TIMESTAMP` | | Substring | `SUBSTRING(s, start, len)` | `SUBSTR(s, start, len)` | `SUBSTR(s, start, len)` | `SUBSTRING(s, start, len)` | `SUBSTRING(s, start, len)` |