Skip to content

CAMEL-24044: camel-sql - Fix Postgres aggregation repositories binding version without a placeholder#24655

Merged
Croway merged 1 commit into
apache:mainfrom
Croway:CAMEL-24044-postgres-aggregation-repository
Jul 13, 2026
Merged

CAMEL-24044: camel-sql - Fix Postgres aggregation repositories binding version without a placeholder#24655
Croway merged 1 commit into
apache:mainfrom
Croway:CAMEL-24044-postgres-aggregation-repository

Conversation

@Croway

@Croway Croway commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

JIRA: https://issues.apache.org/jira/browse/CAMEL-24044

Since the CAMEL-7810 optimistic-locking refactor (Camel 3.4), PostgresAggregationRepository.insert() built INSERT INTO t(exchange, id, ...) ON CONFLICT DO NOTHING without the version column while the base insertHelper binds blob, key and version — one more bound parameter than placeholders, so every add() failed with The column index is out of range (verified against real PostgreSQL). With storeBodyAsText=true the version was silently bound into the body placeholder before failing. ClusteredPostgresAggregationRepository had the same defect in its completed-table insert (every remove() failed at completion) and never wrote the instance_id column, so instance-scoped recovery could never match any row.

Changes:

  • Include the version column in both Postgres INSERT statements and pass the actual version instead of the hardcoded 1L
  • Clustered variant additionally writes instance_id when completed && recoveryByInstance
  • New integration tests against real PostgreSQL (camel-test-infra-postgres) covering the full add/get/update/remove/recover/confirm lifecycle, storeBodyAsText, and instance-scoped recovery with two nodes — these fail on current main and pass with the fix
  • Fixed the stale INSERT example in the component docs and added an upgrade-guide entry (the aggregation tables need the version BIGINT column, as JdbcAggregationRepository already required for reads/updates)

Run the new ITs with:
mvn verify -Pit -Dit.test='PostgresAggregationRepositoryIT,ClusteredPostgresAggregationRepositoryIT' -Dtest=NoSuchTest -Dsurefire.failIfNoSpecifiedTests=false

Claude Code on behalf of Croway

🤖 Generated with Claude Code

@github-actions

Copy link
Copy Markdown
Contributor

🌟 Thank you for your contribution to the Apache Camel project! 🌟
🤖 CI automation will test this PR automatically.

🐫 Apache Camel Committers, please review the following items:

  • First-time contributors require MANUAL approval for the GitHub Actions to run
  • You can use the command /component-test (camel-)component-name1 (camel-)component-name2.. to request a test from the test bot although they are normally detected and executed by CI.
  • You can label PRs using skip-tests and test-dependents to fine-tune the checks executed by this PR.
  • Build and test logs are available in the summary page. Only Apache Camel committers have access to the summary.

⚠️ Be careful when sharing logs. Review their contents before sharing them publicly.

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

Correct fix for a long-standing bug where the Postgres aggregation repositories' INSERT statements were missing the version column (and the clustered variant also missed instance_id), causing parameter-count mismatches on every add() call.

Scanner coverage: No static analysis tools (PMD, Checkstyle, semgrep) available. Rely on CI.

Key observations:

  • The fix correctly aligns the Postgres subclasses' INSERT column lists with the base insertHelper() binding order (exchange blob, key, version, and optionally instance_id).
  • Passing version instead of the hardcoded 1L is correct: the caller provides 1L for new inserts and the actual version from get() for completed-table inserts.
  • The ClusteredPostgresAggregationRepository fix for instance_id is also correct: insertHelper() conditionally binds instanceId when completed && recoveryByInstance, so the SQL must include it under the same condition.
  • No impact on other database dialects — fix is confined to Postgres subclasses.
  • No SQL injection risks — all values are parameterized via PreparedStatement.
  • Integration tests are thorough, covering full lifecycle (add/get/update/remove/scan/recover/confirm), storeBodyAsText mode, and multi-node instance-scoped recovery against real PostgreSQL.
  • Documentation and upgrade guide updates are appropriate.

This review was generated by an AI agent and may contain inaccuracies. Please verify all suggestions before applying.

Claude Code review on behalf of @gnodet

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

Reviewed with focus on the parameter/placeholder arithmetic, both repository flavors, and the recent deserialization-hardening lineage in these classes — the fix is correct and I have no change requests. Summary of the verification:

Root cause confirmed against main. JdbcAggregationRepository.insertHelper (JdbcAggregationRepository.java:336-340) always binds three base parameters (exchange blob → 1, id → 2, version → 3) before the optional body/header ones, while the Postgres subclasses' SQL builders emit only two base columns (EXCHANGE, ID) — one more bound parameter than ? placeholders in every configuration, so add() could never succeed on Postgres. The new column order (EXCHANGE, ID, VERSION, [BODY], [headers...], [instance_id]) exactly matches the binder's positional order in both the plain and clustered insertHelper, and passing the real version (instead of a hardcoded value) matches base-class semantics for the completed-table insert path. The clustered variant's added instance_id column also makes scan()'s WHERE INSTANCE_ID = ? able to match rows for the first time. The regression traces to the Camel 3.4-era CAMEL-7810 rework, which introduced the version binding without adding the placeholder — nothing intentional is being reverted.

Since the broken path could never succeed, there is no behavioral-compat risk — and the documented default DDL already includes version BIGINT NOT NULL, so users following the docs need no schema change. The stale SQL example in sql-component.adoc is corrected too (the old line had the wrong column order and count).

Tests: the two new real-PostgreSQL ITs (plain + clustered, covering add/get/update/remove/scan/recover/confirm, storeBodyAsText, and two-node instance-scoped recovery) are exactly what this fix needed — the H2-based unit tests structurally couldn't catch a Postgres-only SQL defect. No Thread.sleep; new dependencies are test-scoped only (org.postgresql:postgresql via the existing parent property + project-internal camel-test-infra-postgres), matching the established camel-pg-replication-slot pattern.

Security cross-check: the ObjectInputFilter/deserialization-filter work in these classes is untouched (write path only), and values remain ?-bound — only constant identifiers are concatenated into the SQL.

Minor notes, none blocking:

  1. ClusteredPostgresAggregationRepository appends the "instance_id" string literal because the base-class INSTANCE_ID constant is private (ClusteredJdbcAggregationRepository.java:48) — fine as-is; widening the constant to protected would be a reasonable tiny follow-up.
  2. The PR body's mvn verify -Pit references a profile that doesn't exist in this build — failsafe ITs are bound via the always-active full profile, so plain mvn verify runs them; description-only inaccuracy.
  3. The PR body declares "Generated with Claude Code" but the commit lacks the AI co-authorship trailer — per the repo's AI guidelines, worth adding to the squash-merge message.

The two matrix build jobs were still pending at review time — worth confirming green before merge.

Reviewed with Claude Code (Fable 5) on behalf of oscerd. This review was generated by an AI agent and may contain inaccuracies; please verify all suggestions before applying.

@github-actions

Copy link
Copy Markdown
Contributor

🧪 CI tested the following changed modules:

  • components/camel-sql
  • docs

🔬 Scalpel shadow comparison — Scalpel: 12 tested, 28 compile-only — current: 11 all tested

Maveniverse Scalpel detected 40 affected modules (current approach: 11).

⚠️ Modules only in Scalpel (29)
  • apache-camel
  • camel-allcomponents
  • camel-catalog
  • camel-catalog-console
  • camel-catalog-lucene
  • camel-catalog-maven
  • camel-catalog-suggest
  • camel-componentdsl
  • camel-csimple-maven-plugin
  • camel-endpointdsl
  • camel-endpointdsl-support
  • camel-itest
  • camel-jbang-core
  • camel-jbang-it
  • camel-jbang-main
  • camel-jbang-plugin-edit
  • camel-jbang-plugin-generate
  • camel-jbang-plugin-kubernetes
  • camel-jbang-plugin-test
  • camel-kamelet-main
  • camel-launcher
  • camel-report-maven-plugin
  • camel-route-parser
  • camel-yaml-dsl
  • camel-yaml-dsl-deserializers
  • camel-yaml-dsl-maven-plugin
  • coverage
  • docs
  • dummy-component

Skip-tests mode would test 12 modules (2 direct + 10 downstream), skip tests for 28 (generated code, meta-modules)

Modules Scalpel would test (12)
  • camel-jbang-mcp
  • camel-jbang-plugin-mcp
  • camel-jbang-plugin-route-parser
  • camel-jbang-plugin-tui
  • camel-jbang-plugin-validate
  • camel-jta
  • camel-kafka
  • camel-launcher-container
  • camel-sql
  • camel-yaml-dsl-validator
  • camel-yaml-dsl-validator-maven-plugin
  • docs
Modules with tests skipped (28)
  • apache-camel
  • camel-allcomponents
  • camel-catalog
  • camel-catalog-console
  • camel-catalog-lucene
  • camel-catalog-maven
  • camel-catalog-suggest
  • camel-componentdsl
  • camel-csimple-maven-plugin
  • camel-endpointdsl
  • camel-endpointdsl-support
  • camel-itest
  • camel-jbang-core
  • camel-jbang-it
  • camel-jbang-main
  • camel-jbang-plugin-edit
  • camel-jbang-plugin-generate
  • camel-jbang-plugin-kubernetes
  • camel-jbang-plugin-test
  • camel-kamelet-main
  • camel-launcher
  • camel-report-maven-plugin
  • camel-route-parser
  • camel-yaml-dsl
  • camel-yaml-dsl-deserializers
  • camel-yaml-dsl-maven-plugin
  • coverage
  • dummy-component

ℹ️ Shadow mode — Scalpel observes but does not affect test execution. Learn more

All tested modules (40 modules)
  • Camel :: All Components Sync point
  • Camel :: Assembly
  • Camel :: Catalog :: CSimple Maven Plugin (deprecated)
  • Camel :: Catalog :: Camel Catalog
  • Camel :: Catalog :: Camel Report Maven Plugin
  • Camel :: Catalog :: Camel Route Parser
  • Camel :: Catalog :: Console
  • Camel :: Catalog :: Dummy Component
  • Camel :: Catalog :: Lucene (deprecated)
  • Camel :: Catalog :: Maven
  • Camel :: Catalog :: Suggest
  • Camel :: Component DSL
  • Camel :: Coverage
  • Camel :: Docs
  • Camel :: Endpoint DSL
  • Camel :: Endpoint DSL :: Support
  • Camel :: Integration Tests
  • Camel :: JBang :: Core
  • Camel :: JBang :: Integration tests
  • Camel :: JBang :: MCP
  • Camel :: JBang :: Main
  • Camel :: JBang :: Plugin :: Edit
  • Camel :: JBang :: Plugin :: Generate
  • Camel :: JBang :: Plugin :: Kubernetes
  • Camel :: JBang :: Plugin :: MCP
  • Camel :: JBang :: Plugin :: Route Parser
  • Camel :: JBang :: Plugin :: TUI
  • Camel :: JBang :: Plugin :: Testing
  • Camel :: JBang :: Plugin :: Validate
  • Camel :: JTA
  • Camel :: Kafka
  • Camel :: Kamelet Main
  • Camel :: Launcher
  • Camel :: Launcher :: Container
  • Camel :: SQL
  • Camel :: YAML DSL
  • Camel :: YAML DSL :: Deserializers
  • Camel :: YAML DSL :: Maven Plugins
  • Camel :: YAML DSL :: Validator
  • Camel :: YAML DSL :: Validator Maven Plugin

⚙️ View full build and test results

@Croway Croway merged commit 13dc237 into apache:main Jul 13, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants