Skip to content

fix(mappers): don't compare the bigint id column against non-numeric strings in find() - #756

Closed
rubenvdlinde wants to merge 2 commits into
developmentfrom
fix/755-sourcemapper-find-string-lookup
Closed

fix(mappers): don't compare the bigint id column against non-numeric strings in find()#756
rubenvdlinde wants to merge 2 commits into
developmentfrom
fix/755-sourcemapper-find-string-lookup

Conversation

@rubenvdlinde

Copy link
Copy Markdown
Contributor

Closes #755.

Problem

SourceMapper::find(int|string $id) — and the identical pattern in MappingMapper, RuleMapper, JobMapper, EndpointMapper — builds a query that ORs id = :param into the uuid/slug lookup even when the input is a non-numeric string (a uuid or slug):

if (is_string($id) && ctype_digit($id) === false) {
    $qb->where($qb->expr()->orX(
        $qb->expr()->eq('uuid', $qb->createNamedParameter($id)),
        $qb->expr()->eq('slug', $qb->createNamedParameter($id)),
        $qb->expr()->eq('id',   $qb->createNamedParameter($id))   // bigint column vs string param
    ));
}

On Postgres this makes the whole statement fail at plan time:

SQLSTATE[22P02]: invalid input syntax for type bigint: "xwiki"

…so find('<some-slug>') is unusable on a Postgres install — you can only look an entity up by numeric id. (MySQL silently casts the string to 0 and limps along, which is why it hasn't bitten before.)

Fix

Drop the id comparison from the non-numeric branch in all five affected mappers — it can never match a non-numeric string anyway. This brings them in line with SynchronizationMapper::find(), which already only searches uuid/slug for string input. The numeric path (which casts to PARAM_INT) is untouched.

Touched: SourceMapper, MappingMapper, RuleMapper, JobMapper, EndpointMapper.

Why now

OpenRegister's new pluggable integration registry (ConductionNL/openregister#1307, ADR-019) has external integration providers (e.g. the xwiki "Articles" provider) that resolve their OpenConnector source by slug via SourceMapper::find($slug) — see OCA\OpenRegister\Service\Integration\ExternalIntegrationRouter::loadSource(). On a Postgres-backed Nextcloud that 503s with openconnector-source-missing on every call, even when the source exists.

Notes

  • No mapper unit tests in this repo to extend (the tests/ suite mocks services, not the DB layer), so this is verification-by-repro: on Postgres, \OC::$server->get(SourceMapper::class)->find('<existing-source-slug>') throws before this patch and resolves after.
  • Local composer phpcs/psalm couldn't run in my environment (composer.lock is incompatible with PHP 8.2 here — composer update would be needed, which is out of scope for this fix); the change is a one-line deletion per file matching the existing SynchronizationMapper style, so CI's quality jobs should pass clean.

…c strings in find()

`SourceMapper::find()` (and the matching MappingMapper / RuleMapper / JobMapper /
EndpointMapper) build a query that ORs `id = :param` into the uuid/slug lookup even
when the input is a non-numeric string (a uuid or slug). On Postgres that makes the
whole statement fail at plan time:

    SQLSTATE[22P02]: invalid input syntax for type bigint: "xwiki"

so `find('<slug>')` is unusable on Postgres — you can only look an entity up by
numeric id. (MySQL silently casts the string to 0 and limps along.) The `id`
branch can never match a non-numeric string anyway, so drop it from the
non-numeric path — bringing these mappers in line with `SynchronizationMapper::find()`,
which already only searches uuid/slug for string input.

Surfaced by OpenRegister's pluggable integration registry (ConductionNL/openregister#1307):
external integration providers resolve their OpenConnector source by slug via
`SourceMapper::find($slug)` (`ExternalIntegrationRouter::loadSource()`), which 503s
on every Postgres install today.

Fixes #755
@github-actions

Copy link
Copy Markdown
Contributor

Quality Report — ConductionNL/openconnector @ 05cd59e

Check PHP Vue Security License Tests
lint
phpcs
phpmd
psalm
phpstan
phpmetrics
eslint
stylelint
composer ✅ 148/148
npm ❌ 1/573 denied
PHPUnit ⏭️
Newman ⏭️
Playwright ⏭️

❌ Denied npm licenses

Package Version License
@fortawesome/free-solid-svg-icons 6.7.2 (CC-BY-4.0 AND MIT)

Quality workflow — 2026-05-12 13:00 UTC

Download the full PDF report from the workflow artifacts.

Comment thread lib/Db/EndpointMapper.php
@@ -47,12 +47,13 @@ public function find(int|string $id): Endpoint

// If it's a string but can be converted to a numeric value without data loss, use as ID
if (is_string($id) && ctype_digit($id) === false) {

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.

[CONCERN] Negative-integer strings and floats silently return not-found

The guard ctype_digit($id) === false routes strings like "-1", "1.5", or " 123" into the uuid/slug-only branch. Those values can never match a uuid or slug either, so the caller gets a DoesNotExistException rather than a Postgres error — an acceptable silent failure — but it may mask bugs in callers that accidentally pass a stringified negative ID. This applies identically to all five patched mappers. Consider using is_numeric($id) instead of ctype_digit($id) to also handle "-1" and "1.5" correctly, or add a comment noting this behaviour.

Comment thread lib/Db/EndpointMapper.php
@@ -47,12 +47,13 @@ public function find(int|string $id): Endpoint

// If it's a string but can be converted to a numeric value without data loss, use as ID
if (is_string($id) && ctype_digit($id) === false) {

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.

[CONCERN] ctype_digit('007') routes leading-zero strings to numeric branch — wrong match

ctype_digit('007') returns true, so the string '007' is forwarded to the numeric branch and cast to int 7 via PARAM_INT. If the intent is to match by uuid/slug for such values, this would be a mismatch. Also, ctype_digit('') returns false, so an empty string goes to the uuid/slug branch — safe after the fix but worth documenting. Consider is_numeric($id) && (int)$id > 0 as a tighter guard.

@WilcoLouwerse WilcoLouwerse 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.

Review

🟡 Concerns (2)

🟢 Minor (1)

  • Comment consistency across all five patched mappers — verify all updated (lib/Db/EndpointMapper.php:50)
    Verify that the improved comment (explaining the bigint comparison error being avoided) is consistently applied across all five patched mappers: EndpointMapper, JobMapper, MappingMapper, RuleMapper, and SourceMapper. The diff view makes this hard to confirm at a glance.

Reviewed by WilcoLouwerse via automated batch review.

@github-actions

Copy link
Copy Markdown
Contributor

Quality Report — ConductionNL/openconnector @ d94c440

Check PHP Vue Security License Tests
lint
phpcs
phpmd
psalm
phpstan
phpmetrics
eslint
stylelint
composer ✅ 148/148
npm ✅ 674/674
PHPUnit ⏭️
Newman ⏭️
Playwright ⏭️

Quality workflow — 2026-05-19 04:02 UTC

Download the full PDF report from the workflow artifacts.

@rubenvdlinde

Copy link
Copy Markdown
Contributor Author

Closing this PR because all 5 mapper files (SourceMapper, MappingMapper, RuleMapper, JobMapper, EndpointMapper) are deleted in the chain-B/C OR cutover — entity lookup moves to OCA\OpenRegister\Service\ObjectService::find() against magic tables, which doesn't have the bigint/non-numeric-string mismatch this PR fixes. The Postgres bug was real, the fix was correct, and the integration-registry use case (xWiki Articles provider → SourceMapper::find($slug)) carries forward — but now the lookup goes via ObjectService::find($slug, register, schema) which handles slug routing natively. If a regression sneaks in there, the fix would land in openregister/lib/Service/ObjectService.php, not here. Thanks for catching it — the original analysis is what surfaced the Postgres-vs-MySQL silent-cast asymmetry, and that learning should be in the OR test suite.

@rubenvdlinde
rubenvdlinde deleted the fix/755-sourcemapper-find-string-lookup branch May 22, 2026 13:08
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