Skip to content

fix(bigquery): backslash-escape apostrophes in filter values - #38835

Merged
rusackas merged 7 commits into
apache:masterfrom
Krishnachaitanyakc:fix/bigquery-apostrophe-filter-escaping
Jun 27, 2026
Merged

rusackas merged 7 commits into
apache:masterfrom
Krishnachaitanyakc:fix/bigquery-apostrophe-filter-escaping

Conversation

@Krishnachaitanyakc

@Krishnachaitanyakc Krishnachaitanyakc commented Mar 25, 2026 •

Copy link
Copy Markdown
Contributor

Note (updated during review): the final implementation uses BigQuery backslash escaping ('O\'Brien'), not the doubled-quote ('O''Brien') approach the SUMMARY below originally described. Doubled single quotes are not valid in BigQuery. The summary is left intact for history; see the thread for the evolution.


User description

SUMMARY

Fixes #35857

BigQuery errors when dashboard filters on text columns contain apostrophes (e.g. O'Brien, Fernando's).

Root cause: The sqlalchemy-bigquery dialect's process_string_literal function uses Python's repr() to render string literals when literal_binds=True is used during query compilation. When the string contains an apostrophe, repr() wraps the value in double quotes (e.g. repr("O'Brien") -> "O'Brien"). In BigQuery SQL, double-quoted tokens are identifiers (like column or table names), not string literals, so the query fails with a syntax error.

Fix: Monkey-patch the BigQuery dialect's colspecs to use a custom TypeDecorator whose literal_processor always produces single-quoted literals with properly doubled internal quotes ('O''Brien'), which is the standard SQL escaping convention that BigQuery expects. This approach follows the same pattern used for the Databricks engine spec (superset/db_engine_specs/databricks.py).

BEFORE/AFTER SCREENSHOTS OR ANIMATED GIF

Before: Filter with Fernando's generates SQL WHERE name = "Fernando's" (double-quoted identifier, causes BigQuery syntax error)

After: Filter with Fernando's generates SQL WHERE name = 'Fernando''s' (properly escaped single-quoted literal)

TESTING INSTRUCTIONS

  1. Set up a BigQuery connection in Superset
  2. Create a dataset with a text column containing values with apostrophes (e.g. names like O'Brien, Fernando's)
  3. Create a chart using this dataset
  4. Add a filter on the text column selecting a value with an apostrophe
  5. Verify the chart renders without errors
  6. Also verify filters without apostrophes continue to work normally

Unit tests are included:

  • test_string_literal_with_apostrophe - verifies apostrophe escaping
  • test_string_literal_without_apostrophe - verifies normal strings unaffected
  • test_string_literal_in_filter_with_apostrophe - verifies IN clause escaping

ADDITIONAL INFORMATION

  • Has associated issue: BigQuery errors when filters on text columns have apostrophes in them #35857
  • Required feature flags:
  • Changes UI
  • Includes DB Migration (follow approval process in SIP-59)
    • Migration is atomic, supports rollback & is backwards-compatible
    • Confirm DB migration upgrade and downgrade tested
    • Runtime estimates and downtime expectations provided
  • Introduces new feature or API
  • Removes existing feature or API

CodeAnt-AI Description

Escape BigQuery filter values that contain apostrophes

What Changed

  • Filters using names like O'Brien now run in BigQuery instead of failing with a syntax error
  • String values are written with standard single-quote escaping, including values inside multi-select filters
  • Normal text filters without apostrophes still work the same way

Impact

✅ Fewer BigQuery filter errors
✅ Clearer text filtering in dashboards
✅ Reliable filters for names with apostrophes

💡 Usage Guide

Checking Your Pull Request

Every time you make a pull request, our system automatically looks through it. We check for security issues, mistakes in how you're setting up your infrastructure, and common code problems. We do this to make sure your changes are solid and won't cause any trouble later.

Talking to CodeAnt AI

Got a question or need a hand with something in your pull request? You can easily get in touch with CodeAnt AI right here. Just type the following in a comment on your pull request, and replace "Your question here" with whatever you want to ask:

@codeant-ai ask: Your question here

This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.

Example

@codeant-ai ask: Can you suggest a safer alternative to storing this secret?

Preserve Org Learnings with CodeAnt

You can record team preferences so CodeAnt AI applies them in future reviews. Reply directly to the specific CodeAnt AI suggestion (in the same thread) and replace "Your feedback here" with your input:

@codeant-ai: Your feedback here

This helps CodeAnt AI learn and adapt to your team's coding style and standards.

Example

@codeant-ai: Do not flag unused imports.

Retrigger review

Ask CodeAnt AI to review the PR again, by typing:

@codeant-ai: review

Check Your Repository Health

To analyze the health of your code repository, visit our dashboard at https://app.codeant.ai. This tool helps you identify potential issues and areas for improvement in your codebase, ensuring your repository maintains high standards of code health.

@dosubot dosubot Bot added the data:connect:googlebigquery Related to BigQuery label Mar 25, 2026
@codeant-ai-for-open-source codeant-ai-for-open-source Bot added the size:L This PR changes 100-499 lines, ignoring generated files label Mar 25, 2026
@codeant-ai-for-open-source

Copy link
Copy Markdown
Contributor

Sequence Diagram

This PR updates BigQuery SQL compilation so text filter values with apostrophes are always rendered as standard single quoted SQL literals. The flow highlights the new dialect patch and how query compilation now produces BigQuery safe filter SQL.

sequenceDiagram
    participant Superset
    participant BigQueryEngineSpec
    participant BigQueryDialect
    participant SafeStringType
    participant BigQuery

    Superset->>BigQueryEngineSpec: Load BigQuery engine spec
    BigQueryEngineSpec->>BigQueryDialect: Patch string literal handling
    Superset->>BigQueryDialect: Compile query with literal binds
    BigQueryDialect->>SafeStringType: Process text filter value
    SafeStringType-->>BigQueryDialect: Return escaped single quoted literal
    BigQueryDialect->>BigQuery: Execute query with valid filter literal
Loading

Generated by CodeAnt AI

@bito-code-review bito-code-review Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review Agent Run #2b0fe4

Actionable Suggestions - 1
  • superset/db_engine_specs/bigquery.py - 1
Review Details
  • Files reviewed - 2 · Commit Range: 470b90a..470b90a
    • superset/db_engine_specs/bigquery.py
    • tests/unit_tests/db_engine_specs/test_bigquery.py
  • Files skipped - 0
  • Tools
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful
    • MyPy (Static Code Analysis) - ✔︎ Successful
    • Astral Ruff (Static Code Analysis) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers a full AI review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Superset You can customize the agent settings here or contact your Bito workspace admin at evan@preset.io.

Documentation & Help

AI Code Review powered by Bito Logo

Comment thread superset/db_engine_specs/bigquery.py Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes BigQuery query compilation failures when dashboard filter string values contain apostrophes by ensuring string literals are always rendered using standard SQL single-quote escaping under literal_binds=True.

Changes:

  • Add a BigQuery dialect monkeypatch that overrides string literal rendering to always use single-quoted SQL literals with doubled internal quotes.
  • Add unit tests intended to validate correct compilation for = and IN (...) filters with and without apostrophes.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
superset/db_engine_specs/bigquery.py Monkeypatches sqlalchemy-bigquery string literal rendering via a TypeDecorator and custom literal processor.
tests/unit_tests/db_engine_specs/test_bigquery.py Adds tests for compiled SQL string literals containing apostrophes and for IN-clause escaping.

Comment thread superset/db_engine_specs/bigquery.py Outdated
Comment thread tests/unit_tests/db_engine_specs/test_bigquery.py Outdated
@codecov

codecov Bot commented Mar 25, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 46.15385% with 14 lines in your changes missing coverage. Please review.
✅ Project coverage is 64.36%. Comparing base (9e130e5) to head (da61264).
⚠️ Report is 12 commits behind head on master.

Files with missing lines Patch % Lines
superset/db_engine_specs/bigquery.py 46.15% 14 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master   #38835      +/-   ##
==========================================
- Coverage   64.36%   64.36%   -0.01%     
==========================================
  Files        2653     2653              
  Lines      144869   144892      +23     
  Branches    33424    33425       +1     
==========================================
+ Hits        93247    93256       +9     
- Misses      49951    49963      +12     
- Partials     1671     1673       +2     
Flag Coverage Δ
hive 39.34% <46.15%> (+0.01%) ⬆️
mysql 58.05% <46.15%> (+<0.01%) ⬆️
postgres 58.11% <46.15%> (-0.01%) ⬇️
presto 40.92% <46.15%> (+<0.01%) ⬆️
python 59.56% <46.15%> (-0.01%) ⬇️
sqlite 57.77% <46.15%> (+<0.01%) ⬆️
unit 100.00% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@netlify

netlify Bot commented Apr 6, 2026 •

Copy link
Copy Markdown

✅ Deploy Preview for superset-docs-preview ready!

Name Link
🔨 Latest commit da61264
🔍 Latest deploy log https://app.netlify.com/projects/superset-docs-preview/deploys/6a37359d8066ca0008f4a31c
😎 Deploy Preview https://deploy-preview-38835--superset-docs-preview.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@bito-code-review

bito-code-review Bot commented Apr 6, 2026 •

Copy link
Copy Markdown
Contributor

Code Review Agent Run #40e3ca

Actionable Suggestions - 0
Review Details
  • Files reviewed - 2 · Commit Range: 470b90a..182e8a2
    • superset/db_engine_specs/bigquery.py
    • tests/unit_tests/db_engine_specs/test_bigquery.py
  • Files skipped - 0
  • Tools
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful
    • MyPy (Static Code Analysis) - ✔︎ Successful
    • Astral Ruff (Static Code Analysis) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers a full AI review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Superset You can customize the agent settings here or contact your Bito workspace admin at evan@preset.io.

Documentation & Help

AI Code Review powered by Bito Logo

@bito-code-review

bito-code-review Bot commented Apr 6, 2026 •

Copy link
Copy Markdown
Contributor

Code Review Agent Run #172fb3

Actionable Suggestions - 0
Review Details
  • Files reviewed - 1 · Commit Range: 182e8a2..8e22201
    • tests/unit_tests/db_engine_specs/test_bigquery.py
  • Files skipped - 0
  • Tools
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful
    • MyPy (Static Code Analysis) - ✔︎ Successful
    • Astral Ruff (Static Code Analysis) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers a full AI review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Superset You can customize the agent settings here or contact your Bito workspace admin at evan@preset.io.

Documentation & Help

AI Code Review powered by Bito Logo

@rusackas

Copy link
Copy Markdown
Member

@Krishnachaitanyakc I think this produces the wrong escape for BigQuery. The query in #35857 that fails is IN ('Armando''s') — already doubled single quotes — and BigQuery rejects it with "concatenated string literals must be separated by whitespace", because it reads 'Armando''s' as 'Armando' next to 's'. BigQuery escapes a quote with a backslash ('O\'Brien'), not by doubling. So _process_string_literal returning 'O''Brien' reproduces the reported error rather than fixing it. The unit tests pass only because they assert the string shape, not that BigQuery accepts it. Am I reading the issue wrong?

@rusackas

Copy link
Copy Markdown
Member

@Krishnachaitanyakc I think this produces the wrong escape for BigQuery. The query in #35857 that fails is IN ('Armando''s') is already doubled single quotes. BigQuery rejects it with "concatenated string literals must be separated by whitespace", because it reads 'Armando''s' as 'Armando' next to 's'. BigQuery escapes a quote with a backslash ('O\'Brien'), not by doubling. So _process_string_literal returning 'O''Brien' reproduces the reported error rather than fixing it. The unit tests pass only because they assert the string shape, not that BigQuery accepts it. Am I reading the issue wrong?

@Krishnachaitanyakc

Copy link
Copy Markdown
Contributor Author

@rusackas Thanks for catching this. I've pushed a fix that switches _process_string_literal to use ' instead of '', escapes existing backslashes first, and removes the unnecessary % → %% replacement.

Krishnachaitanyakc and others added 6 commits June 19, 2026 13:18
… quoting

The sqlalchemy-bigquery dialect uses Python's repr() to render string
literals when literal_binds=True.  repr() switches to double-quote
delimiters when the string contains an apostrophe (e.g. repr("O'Brien")
produces "O'Brien").  In BigQuery SQL, double-quoted tokens are
identifiers, not string literals, so any filter containing an apostrophe
causes a syntax error.

This patch monkey-patches the BigQuery dialect's colspecs to use a custom
string type whose literal_processor always produces single-quoted literals
with properly doubled internal quotes (standard SQL escaping).

Fixes apache#35857
Add direct tests for _process_string_literal, non-BigQuery dialect
fallback path in BigQuerySafeString.literal_processor, and monkeypatch
verification to address missing coverage lines. Apply ruff formatting
fix to bigquery.py.
Remove unused `type: ignore` comments from BigQuerySafeString class
definition and colspecs assignment. Replace bare `assert BigQueryEngineSpec`
with `assert BigQueryEngineSpec is not None` to satisfy mypy's
truthy-function check.
Add tests for BigQuerySafeString.literal_processor bigquery branch
and the ImportError fallback path to close Codecov patch coverage gaps.
BigQuery does not support doubled single-quote escaping ('O''Brien').
It parses this as two concatenated string literals without whitespace,
causing: 'concatenated string literals must be separated by whitespace'.

BigQuery requires backslash escaping ('O\'Brien') per its lexical rules.

Changes:
- _process_string_literal now uses backslash escaping (\' instead of '')
- Escape existing backslashes first (\ -> \\) before apostrophes
- Remove unnecessary percent escaping (BigQuery does not require it)
- Update all unit tests to assert backslash-escaped output
- Add negative assertions rejecting doubled-quote output
- Add edge-case coverage: backslash-in-value, percent passthrough

Follows the same pattern as the Databricks dialect fix
(ParamEscaper.escape_string in databricks.py).
@Krishnachaitanyakc
Krishnachaitanyakc force-pushed the fix/bigquery-apostrophe-filter-escaping branch from 8ca42b8 to af8b50d Compare June 19, 2026 18:20
@bito-code-review

bito-code-review Bot commented Jun 19, 2026 •

Copy link
Copy Markdown
Contributor

Code Review Agent Run #97e331

Actionable Suggestions - 0
Review Details
  • Files reviewed - 2 · Commit Range: 647bc9c..af8b50d
    • superset/db_engine_specs/bigquery.py
    • tests/unit_tests/db_engine_specs/test_bigquery.py
  • Files skipped - 0
  • Tools
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful
    • MyPy (Static Code Analysis) - ✔︎ Successful
    • Astral Ruff (Static Code Analysis) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers a full AI review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Superset You can customize the agent settings here or contact your Bito workspace admin at evan@preset.io.

Documentation & Help

AI Code Review powered by Bito Logo

@rusackas

Copy link
Copy Markdown
Member

Fresh codex review for ya:

This fixes the apostrophe case, but I think the new literal processor regresses other string values that repr() handled for us before.

_process_string_literal() only escapes backslashes and apostrophes:

escaped = value.replace("\\", "\\\\").replace("'", "\\'")
return f"'{escaped}'"

For a value containing an actual newline, e.g. "foo\nbar", this emits a literal newline inside a single-quoted BigQuery string:

'foo
bar'

BigQuery rejects that form. Its lexical docs say quoted strings cannot contain newlines, and newlines need to be represented with escape sequences such as \n: https://docs.cloud.google.com/bigquery/docs/reference/standard-sql/lexical#string_and_bytes_literals

Could we preserve escaping for control characters (\n, \r, \t, etc.) while still forcing single-quoted literals and escaping apostrophes with \'? It would also be good to add a regression test for a value containing a newline.

The previous backslash-escape fix only handled apostrophes and
backslashes, so values containing real newlines, carriage returns, tabs,
or other control characters were emitted as literal bytes inside a
single-quoted BigQuery literal.  BigQuery rejects that with
"quoted strings cannot contain newlines" per its lexical rules.

This change introduces an explicit escape table keyed off BigQuery's
documented escape sequences (\n, \r, \t, \b, \f, \v, \a, \\, \') and
falls back to \\xhh for any remaining C0 control character or DEL.
\? \" and \` are intentionally omitted because they do not require
escaping inside a single-quoted literal, and \0 is intentionally absent
because BigQuery requires exactly three octal digits — the null byte
falls through to the \\xhh fallback and is emitted as \\x00.

Tests now cover:
- exact-equality assertions for every named escape and several \\xhh
  fallback cases (null, 0x01, ESC, DEL)
- literal backslash-then-n to confirm it does not collapse to a newline
- double-quote pass-through
- a negative assertion that no literal control character leaks into the
  output for any of the affected characters
- an end-to-end SQLAlchemy compile-through-dialect test for a filter
  value containing an embedded newline
@Krishnachaitanyakc

Copy link
Copy Markdown
Contributor Author

@rusackas Got it. Made changes with explicit escape table for BigQuery's named escapes (\n, \r, \t, \b, \f, \v, \a, \, ') with a \xhh fallback for any other C0/DEL. Please review

@bito-code-review

bito-code-review Bot commented Jun 21, 2026 •

Copy link
Copy Markdown
Contributor

Code Review Agent Run #d32113

Actionable Suggestions - 0
Review Details
  • Files reviewed - 2 · Commit Range: af8b50d..da61264
    • superset/db_engine_specs/bigquery.py
    • tests/unit_tests/db_engine_specs/test_bigquery.py
  • Files skipped - 0
  • Tools
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful
    • MyPy (Static Code Analysis) - ✔︎ Successful
    • Astral Ruff (Static Code Analysis) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers a full AI review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Superset You can customize the agent settings here or contact your Bito workspace admin at evan@preset.io.

Documentation & Help

AI Code Review powered by Bito Logo

@rusackas rusackas left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @Krishnachaitanyakc, LGTM! Backslash escaping is the right call (the doubled-quote version was the original bug), and the control-char escape table is a nice touch.

Heads up, I fixed up the squash commit message on merge, since the PR description still describes the old 'O''Brien' approach we abandoned. Merging now... thanks for sticking through the review rounds :)

@rusackas rusackas changed the title fix(bigquery): escape apostrophes in filter values using standard SQL quoting fix(bigquery): backslash-escape apostrophes in filter values Jun 27, 2026
@rusackas
rusackas merged commit a147079 into apache:master Jun 27, 2026
61 checks passed
rusackas added a commit that referenced this pull request Sep 23, 2026
…QL engine

Adds testcontainers coverage for superset/db_engine_specs/bigquery.py's
_monkeypatch_bigquery_string_literal, using the new BigQueryContainer
(testcontainers/testcontainers-python#1121) to run apostrophe, percent-sign,
and combined-value queries against a real GoogleSQL emulator rather than
reasoning about the sqlalchemy-bigquery dialect and BigQuery DBAPI paramstyle
handling from source alone. Also pins down, with a direct reproduction, why
the doubled-single-quote escape #38835 replaced doesn't work on BigQuery.

Co-Authored-By: Evan Rusackas <evan@preset.io>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
rusackas added a commit that referenced this pull request Sep 23, 2026
…QL engine

Adds testcontainers coverage for superset/db_engine_specs/bigquery.py's
_monkeypatch_bigquery_string_literal, using the new BigQueryContainer
(testcontainers/testcontainers-python#1121) to run apostrophe, percent-sign,
and combined-value queries against a real GoogleSQL emulator rather than
reasoning about the sqlalchemy-bigquery dialect and BigQuery DBAPI paramstyle
handling from source alone. Also pins down, with a direct reproduction, why
the doubled-single-quote escape #38835 replaced doesn't work on BigQuery.

Co-Authored-By: Evan Rusackas <evan@preset.io>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
rusackas added a commit that referenced this pull request Oct 1, 2026
…QL engine

Adds testcontainers coverage for superset/db_engine_specs/bigquery.py's
_monkeypatch_bigquery_string_literal, using the new BigQueryContainer
(testcontainers/testcontainers-python#1121) to run apostrophe, percent-sign,
and combined-value queries against a real GoogleSQL emulator rather than
reasoning about the sqlalchemy-bigquery dialect and BigQuery DBAPI paramstyle
handling from source alone. Also pins down, with a direct reproduction, why
the doubled-single-quote escape #38835 replaced doesn't work on BigQuery.

Co-Authored-By: Evan Rusackas <evan@preset.io>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

data:connect:googlebigquery Related to BigQuery size/L size:L This PR changes 100-499 lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

BigQuery errors when filters on text columns have apostrophes in them

3 participants