Skip to content

Comments

fix(import): use UPSERT for specs to handle duplicate record IDs#15

Merged
CamonZ merged 2 commits intoCamonZ:masterfrom
churcho:fix/upsert-duplicate-specs-pr
Feb 19, 2026
Merged

fix(import): use UPSERT for specs to handle duplicate record IDs#15
CamonZ merged 2 commits intoCamonZ:masterfrom
churcho:fix/upsert-duplicate-specs-pr

Conversation

@churcho
Copy link
Contributor

@churcho churcho commented Feb 17, 2026

Problem

Import can fail when ex_ast extracts both a @callback and a @spec for the same function (same module, name, arity), because both entries can produce clause_index = 0.

That generates the same SurrealDB record ID twice:

specs:[module_name, function_name, arity, clause_index]

So the second write fails with:
Database record ... already exists.

Example

In MyApp.Integrations.UrlShortener:

@callback shorten(url :: String.t(), config :: config(), opts :: opts()) ::
  {:ok, String.t()} | {:error, term()}

@spec shorten(String.t(), config(), opts()) ::
  {:ok, String.t()} | {:error, term()}

Both extracted entries map to:
specs:['MyApp.Integrations.UrlShortener', 'shorten', 3, 0]

With CREATE, the second insert errors.

Root Cause

import_specs used:

  • CREATE specs:[$module_name, $function_name, $arity, $clause_index] SET ...

CREATE is not idempotent for duplicate natural keys, even when duplicate rows are valid extraction output (@callback + @spec).

Fix

Switched CREATE to UPSERT in import_specs (db/src/queries/import.rs):

  • UPSERT specs:[$module_name, $function_name, $arity, $clause_index] SET ...

This makes duplicate keys safe: the later row updates the same record instead of failing import.

Why This Is Safe

  • No behavior change for non-duplicate imports.
  • Duplicate natural keys no longer crash import.
  • Key design stays the same (module + function + arity + clause_index).

Validation

Added regression test:

  • test_import_specs_upserts_duplicate_record_ids in db/src/queries/import.rs

The test verifies:

  • import succeeds when duplicate spec IDs are present
  • only one row exists for the duplicate key
  • final stored row reflects overwrite behavior

Executed:

  • cargo test -p db import_specs (pass)

@CamonZ CamonZ merged commit 030eed7 into CamonZ:master Feb 19, 2026
1 check passed
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.

2 participants