Skip to content

fix: parser treats 'atomic' in identifiers as BEGIN ATOMIC keyword#4747

Draft
7ttp wants to merge 3 commits intosupabase:developfrom
7ttp:fix/parser-atomic-final
Draft

fix: parser treats 'atomic' in identifiers as BEGIN ATOMIC keyword#4747
7ttp wants to merge 3 commits intosupabase:developfrom
7ttp:fix/parser-atomic-final

Conversation

@7ttp
Copy link
Contributor

@7ttp 7ttp commented Jan 21, 2026

Summary

Parser incorrectly treats atomic substring in identifiers as BEGIN ATOMIC keyword, causing migration failures.

Problem

Migrations with table/column names containing atomic fail with:
ERROR: cannot insert multiple commands into a prepared statement (SQLSTATE 42601)

Example:

create table public.atomic_test (id int);
select 1;

Root cause: Parser matches atomic in identifiers as the BEGIN ATOMIC keyword, enters a state where semicolons are ignored, and merges multiple statements into one.

Solution

Added word boundary check: ATOMIC only recognized as keyword when preceded by whitespace.
This prevents false matches in identifiers while preserving correct BEGIN ATOMIC function body handling.

Related

Summary by CodeRabbit

  • Bug Fixes

    • Improved ATOMIC block detection to require whitespace before the keyword, preventing false detection when "atomic" appears within identifiers like table names, function names, or column names.
  • Tests

    • Added comprehensive test coverage for ATOMIC block parsing with "atomic" appearing in various identifier contexts.

@7ttp 7ttp requested a review from a team as a code owner January 21, 2026 20:24
Copy link

@xdmden xdmden left a comment

Choose a reason for hiding this comment

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

Nice fix! The whitespace boundary check is the right approach. Two suggestions:

  1. Simplify and fix offset == 0 edge case — when offset == 0, there's no BEGIN before ATOMIC, so it should never enter AtomicState. Changing offset >= 0 to offset > 0 handles this and lets us collapse the nested ifs into one condition. Since whitespace characters are all ASCII, we can also skip the rune decode and just cast the byte directly.

  2. More test coverage — the current test covers atomic_test (prefix), but it's worth covering suffix (my_function_atomic), mid-identifier (is_atomic), and digit-boundary (col1atomic) cases too, since these are all patterns users are likely to hit.

Co-authored-by: Dmitry Deniskin <166256275+xdmden@users.noreply.github.com>
@coderabbitai
Copy link

coderabbitai bot commented Feb 13, 2026

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Modified the ATOMIC block detection logic in the parser to require a whitespace character immediately before the BEGIN_ATOMIC keyword, preventing false matches when "atomic" appears as part of identifiers. Added test cases to verify that "atomic" substrings in table names, function names, and column names do not trigger atomic block parsing.

Changes

Cohort / File(s) Summary
Parser ATOMIC detection
pkg/parser/state.go
Added whitespace validation check before BEGIN_ATOMIC keyword matching to prevent matching "atomic" within identifiers like table names.
Parser test coverage
pkg/parser/state_test.go
Added four new test subtests verifying that "atomic" substrings in table names, function names, column names, and digit-prefixed identifiers do not prematurely trigger atomic block state transitions.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 2
❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Merge Conflict Detection ⚠️ Warning ❌ Merge conflicts detected (83 files):

⚔️ .github/workflows/ci.yml (content)
⚔️ .github/workflows/deploy.yml (content)
⚔️ .github/workflows/mirror-image.yml (content)
⚔️ README.md (content)
⚔️ cmd/branches.go (content)
⚔️ cmd/db.go (content)
⚔️ cmd/domains.go (content)
⚔️ cmd/encryption.go (content)
⚔️ cmd/functions.go (content)
⚔️ cmd/gen.go (content)
⚔️ cmd/vanitySubdomains.go (content)
⚔️ go.mod (content)
⚔️ go.sum (content)
⚔️ internal/backups/list/list.go (content)
⚔️ internal/backups/restore/restore.go (content)
⚔️ internal/bans/get/get.go (content)
⚔️ internal/branches/create/create.go (content)
⚔️ internal/branches/delete/delete.go (content)
⚔️ internal/branches/disable/disable.go (content)
⚔️ internal/branches/list/list.go (content)
⚔️ internal/branches/update/update.go (content)
⚔️ internal/config/push/push_test.go (content)
⚔️ internal/db/diff/diff.go (content)
⚔️ internal/db/diff/pgdelta.go (content)
⚔️ internal/db/diff/templates/pgdelta.ts (content)
⚔️ internal/db/pull/pull.go (content)
⚔️ internal/db/reset/reset.go (content)
⚔️ internal/db/test/test.go (content)
⚔️ internal/encryption/get/get.go (content)
⚔️ internal/encryption/get/get_test.go (content)
⚔️ internal/encryption/update/update.go (content)
⚔️ internal/encryption/update/update_test.go (content)
⚔️ internal/functions/download/download.go (content)
⚔️ internal/functions/list/list.go (content)
⚔️ internal/functions/list/list_test.go (content)
⚔️ internal/functions/new/templates/deno.json (content)
⚔️ internal/functions/new/templates/index.ts (content)
⚔️ internal/functions/serve/serve.go (content)
⚔️ internal/functions/serve/templates/main.ts (content)
⚔️ internal/gen/signingkeys/signingkeys.go (content)
⚔️ internal/gen/signingkeys/signingkeys_test.go (content)
⚔️ internal/hostnames/activate/activate.go (content)
⚔️ internal/hostnames/common.go (content)
⚔️ internal/hostnames/common_test.go (content)
⚔️ internal/hostnames/create/create.go (content)
⚔️ internal/hostnames/delete/delete.go (content)
⚔️ internal/hostnames/get/get.go (content)
⚔️ internal/hostnames/reverify/reverify.go (content)
⚔️ internal/migration/apply/apply.go (content)
⚔️ internal/postgresConfig/delete/delete.go (content)
⚔️ internal/postgresConfig/get/get.go (content)
⚔️ internal/postgresConfig/update/update.go (content)
⚔️ internal/snippets/download/download.go (content)
⚔️ internal/snippets/list/list.go (content)
⚔️ internal/ssl_enforcement/get/get.go (content)
⚔️ internal/ssl_enforcement/update/update.go (content)
⚔️ internal/start/start.go (content)
⚔️ internal/testing/apitest/platform.go (content)
⚔️ internal/utils/connect.go (content)
⚔️ internal/utils/misc.go (content)
⚔️ internal/utils/prompt.go (content)
⚔️ internal/vanity_subdomains/activate/activate.go (content)
⚔️ internal/vanity_subdomains/check/check.go (content)
⚔️ internal/vanity_subdomains/delete/delete.go (content)
⚔️ internal/vanity_subdomains/get/get.go (content)
⚔️ package.json (content)
⚔️ pkg/config/api.go (content)
⚔️ pkg/config/apikeys.go (content)
⚔️ pkg/config/auth.go (content)
⚔️ pkg/config/config.go (content)
⚔️ pkg/config/db.go (content)
⚔️ pkg/config/storage.go (content)
⚔️ pkg/config/templates/Dockerfile (content)
⚔️ pkg/config/templates/config.toml (content)
⚔️ pkg/config/testdata/config.toml (content)
⚔️ pkg/config/updater.go (content)
⚔️ pkg/go.mod (content)
⚔️ pkg/go.sum (content)
⚔️ pkg/migration/dump.go (content)
⚔️ pkg/migration/file.go (content)
⚔️ pkg/migration/file_test.go (content)
⚔️ pkg/parser/state.go (content)
⚔️ pkg/parser/state_test.go (content)

These conflicts must be resolved before merging into develop.
Resolve conflicts locally and push changes to this branch.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately and concisely describes the main fix: adding a word-boundary check so 'atomic' in identifiers is not treated as the BEGIN ATOMIC keyword.
Linked Issues check ✅ Passed The changes implement the required fix by adding a whitespace-check condition before BEGIN_ATOMIC recognition, directly addressing the parser bug reported in issue #4746.
Out of Scope Changes check ✅ Passed All changes are narrowly scoped to the parser bug fix and its test coverage, with no unrelated modifications.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@pkg/parser/state.go`:
- Around line 49-53: The current check that enters AtomicState only validates
the preceding boundary and still misclassifies identifiers that start with
"atomic" (e.g., "atomic_test"); update the logic around the detection that uses
BEGIN_ATOMIC/END_ATOMIC so it also validates the trailing boundary before
returning AtomicState: either peek the rune(s) immediately after the matched
"atomic" and ensure the next rune is not an identifier character (letters,
digits, or '_'), or introduce a small candidate state that consumes the
following rune and only transitions to AtomicState if that rune is not an
identifier char; reference the existing AtomicState type and the
BEGIN_ATOMIC/END_ATOMIC constants and ensure you use the same
identifier-character test used elsewhere (e.g., unicode.IsLetter/IsDigit or the
repo's isIdentChar helper) when deciding the trailing boundary.
- Around line 49-53: Replace the duplicated nested if with the intended single
condition: remove the original if using offset >= 0 and keep the new combined
condition (offset > 0 && strings.EqualFold(string(data[offset:]), BEGIN_ATOMIC)
&& unicode.IsSpace(rune(data[offset-1]))), so the function returns
&AtomicState{prev: s, delimiter: []byte(END_ATOMIC)} under that single check;
ensure only one if block surrounds that return and that BEGIN_ATOMIC/END_ATOMIC
and AtomicState are referenced as in the diff.
🧹 Nitpick comments (1)
pkg/parser/state_test.go (1)

171-191: Add coverage for identifiers that start with atomic (no schema prefix).

The new cases cover "atomic" inside identifiers, but not the prefix case like atomic_test, which is still a likely false-positive. Adding a test here will lock the fix.

✅ Suggested test
 	t.Run("atomic in table name", func(t *testing.T) {
 		sql := []string{"create table public.atomic_test (id int);", "\nselect 1;"}
 		checkSplit(t, sql)
 	})
+
+	t.Run("atomic prefix in identifier", func(t *testing.T) {
+		sql := []string{"create table atomic_test (id int);", "\nselect 1;"}
+		checkSplit(t, sql)
+	})

@7ttp 7ttp marked this pull request as draft February 13, 2026 08:33
@7ttp 7ttp self-assigned this Feb 13, 2026
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Database migration fails on local server when table or column name contains 'atomic'

2 participants