This is an automated email from the ASF dual-hosted git repository.

gnodet pushed a commit to branch camel-4.22.x
in repository https://gitbox.apache.org/repos/asf/camel.git


The following commit(s) were added to refs/heads/camel-4.22.x by this push:
     new 6c9af81bd04a [backport camel-4.22.x] CAMEL-24801: camel-sql - 
sql-stored: allow parentheses inside simple expressions in the template grammar 
(#26563)
6c9af81bd04a is described below

commit 6c9af81bd04ae23635236b70cceb7c20f1324905
Author: Guillaume Nodet <[email protected]>
AuthorDate: Thu Sep 17 21:31:20 2026 +0200

    [backport camel-4.22.x] CAMEL-24801: camel-sql - sql-stored: allow 
parentheses inside simple expressions in the template grammar (#26563)
---
 .../camel/catalog/docs/sql-stored-component.adoc   |  5 ++
 .../template/generated/SSPTParserTokenManager.java |  2 +-
 .../src/main/docs/sql-stored-component.adoc        |  5 ++
 .../component/sql/stored/template/grammar/sspt.jj  |  2 +-
 .../camel/component/sql/stored/ParserTest.java     | 30 ++++++++
 .../sql/stored/ProducerSimpleFunctionTest.java     | 81 ++++++++++++++++++++++
 6 files changed, 123 insertions(+), 2 deletions(-)

diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/sql-stored-component.adoc
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/sql-stored-component.adoc
index db0b164709ff..7a5b45e517d6 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/sql-stored-component.adoc
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/sql-stored-component.adoc
@@ -119,6 +119,11 @@ It can be either a Simple expression or header location 
i.e. `:#<header name>`.
 the Simple expression `${header.val}` would mean that parameter value will be 
read from the header `val`.
 Header location expression `:#val` would have identical effect.
 
+The Simple expression may use functions with a single argument, such as 
`${val(1)}` or `${bodyAs(String)}`.
+However, the template is not a full Simple expression: functions with 
multiple, comma-separated arguments (such as `${replace(a,b)}`)
+and nested functions are not supported, as the comma is used to separate the 
parameters of the stored procedure.
+If you need such expressions, then compute the value beforehand, for example 
into a header, and refer to it with `${header.name}`.
+
 When using named parameters, Camel will look up the names in the given 
precedence:
 
 1. from a xref:languages:simple-language.adoc[Simple] expressions
diff --git 
a/components/camel-sql/src/generated/java/org/apache/camel/component/sql/stored/template/generated/SSPTParserTokenManager.java
 
b/components/camel-sql/src/generated/java/org/apache/camel/component/sql/stored/template/generated/SSPTParserTokenManager.java
index 5640f750c7c0..0a77969e6687 100644
--- 
a/components/camel-sql/src/generated/java/org/apache/camel/component/sql/stored/template/generated/SSPTParserTokenManager.java
+++ 
b/components/camel-sql/src/generated/java/org/apache/camel/component/sql/stored/template/generated/SSPTParserTokenManager.java
@@ -308,7 +308,7 @@ private int jjMoveNfa_0(int startState, int curPos)
                   { jjCheckNAdd(4); }
                   break;
                case 6:
-                  if ((0x7ff609d00000000L & l) != 0L)
+                  if ((0x7ff639d00000000L & l) != 0L)
                      { jjAddStates(6, 7); }
                   break;
                case 9:
diff --git a/components/camel-sql/src/main/docs/sql-stored-component.adoc 
b/components/camel-sql/src/main/docs/sql-stored-component.adoc
index db0b164709ff..7a5b45e517d6 100644
--- a/components/camel-sql/src/main/docs/sql-stored-component.adoc
+++ b/components/camel-sql/src/main/docs/sql-stored-component.adoc
@@ -119,6 +119,11 @@ It can be either a Simple expression or header location 
i.e. `:#<header name>`.
 the Simple expression `${header.val}` would mean that parameter value will be 
read from the header `val`.
 Header location expression `:#val` would have identical effect.
 
+The Simple expression may use functions with a single argument, such as 
`${val(1)}` or `${bodyAs(String)}`.
+However, the template is not a full Simple expression: functions with 
multiple, comma-separated arguments (such as `${replace(a,b)}`)
+and nested functions are not supported, as the comma is used to separate the 
parameters of the stored procedure.
+If you need such expressions, then compute the value beforehand, for example 
into a header, and refer to it with `${header.name}`.
+
 When using named parameters, Camel will look up the names in the given 
precedence:
 
 1. from a xref:languages:simple-language.adoc[Simple] expressions
diff --git 
a/components/camel-sql/src/main/java/org/apache/camel/component/sql/stored/template/grammar/sspt.jj
 
b/components/camel-sql/src/main/java/org/apache/camel/component/sql/stored/template/grammar/sspt.jj
index 85d5d1d87a67..ed47afeb44ee 100644
--- 
a/components/camel-sql/src/main/java/org/apache/camel/component/sql/stored/template/grammar/sspt.jj
+++ 
b/components/camel-sql/src/main/java/org/apache/camel/component/sql/stored/template/grammar/sspt.jj
@@ -230,7 +230,7 @@ TOKEN: {
 }
 
 TOKEN : {
-    <SIMPLE_EXP_TOKEN: "${"(<LETTER>|<DIGIT> | <SPECIAL> | " ")+ "}">
+    <SIMPLE_EXP_TOKEN: "${"(<LETTER>|<DIGIT> | <SPECIAL> | "(" | ")" | " ")+ 
"}">
 }
 
 TOKEN : {
diff --git 
a/components/camel-sql/src/test/java/org/apache/camel/component/sql/stored/ParserTest.java
 
b/components/camel-sql/src/test/java/org/apache/camel/component/sql/stored/ParserTest.java
index c2be990a25b4..101fb9e64b26 100644
--- 
a/components/camel-sql/src/test/java/org/apache/camel/component/sql/stored/ParserTest.java
+++ 
b/components/camel-sql/src/test/java/org/apache/camel/component/sql/stored/ParserTest.java
@@ -112,6 +112,36 @@ class ParserTest extends CamelTestSupport {
         assertEquals(3, ((InParameter) 
template.getParameterList().get(1)).getValueExtractor().eval(exchange, null));
     }
 
+    @Test
+    public void simpleFunctionWithParentheses() {
+        Exchange exchange = createExchangeWithBody(42);
+        exchange.getIn().setHeader("bar", 3);
+        Template template = parser.parseTemplate(
+                "ADDNUMBERS2(INTEGER ${val(1)},VARCHAR ${bodyAs(String)},INOUT 
INTEGER ${val(7)} inout1,OUT INTEGER out1)");
+
+        assertEquals(4, template.getParameterList().size());
+        assertEquals("1", ((InParameter) 
template.getParameterList().get(0)).getValueExtractor().eval(exchange, null));
+        assertEquals("42", ((InParameter) 
template.getParameterList().get(1)).getValueExtractor().eval(exchange, null));
+        assertEquals("7", ((InOutParameter) 
template.getParameterList().get(2)).getValueExtractor().eval(exchange, null));
+        assertEquals("out1", ((OutParameter) 
template.getParameterList().get(3)).getOutValueMapKey());
+    }
+
+    @Test
+    public void simpleFunctionAsLastParameter() {
+        // the closing parenthesis of the function must not be confused with 
the end of the procedure
+        Exchange exchange = createExchangeWithBody(1);
+        Template template = parser.parseTemplate("ADDNUMBERS2(INTEGER 
${val(1)} )");
+        assertEquals(1, template.getParameterList().size());
+        assertEquals("1", ((InParameter) 
template.getParameterList().get(0)).getValueExtractor().eval(exchange, null));
+    }
+
+    @Test
+    public void multiArgFunctionShouldFail() {
+        // the comma separates the procedure parameters, so functions with 
several arguments are not supported
+        assertThrows(ParseRuntimeException.class,
+                () -> parser.parseTemplate("ADDNUMBERS2(VARCHAR 
${replace(a,b)})"));
+    }
+
     @Test
     public void vendorSpecificPositiveSqlType() {
         Template template = parser.parseTemplate("ADDNUMBERS2(1342 
${header.foo})");
diff --git 
a/components/camel-sql/src/test/java/org/apache/camel/component/sql/stored/ProducerSimpleFunctionTest.java
 
b/components/camel-sql/src/test/java/org/apache/camel/component/sql/stored/ProducerSimpleFunctionTest.java
new file mode 100644
index 000000000000..07b00f9bef51
--- /dev/null
+++ 
b/components/camel-sql/src/test/java/org/apache/camel/component/sql/stored/ProducerSimpleFunctionTest.java
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.sql.stored;
+
+import java.util.Map;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.test.junit6.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase;
+import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
+import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * Simple functions with a single argument, such as ${val(1)}, can be used as 
parameter value in the template.
+ */
+public class ProducerSimpleFunctionTest extends CamelTestSupport {
+
+    EmbeddedDatabase db;
+
+    @Override
+    public void doPreSetup() throws Exception {
+        db = new EmbeddedDatabaseBuilder()
+                .setName(getClass().getSimpleName())
+                .setType(EmbeddedDatabaseType.HSQL)
+                .addScript("sql/storedProcedureTest.sql").build();
+    }
+
+    @Override
+    public void doPostTearDown() throws Exception {
+        if (db != null) {
+            db.shutdown();
+        }
+    }
+
+    @Test
+    public void shouldExecuteStoredProcedureWithSimpleFunction() throws 
InterruptedException {
+        MockEndpoint mock = getMockEndpoint("mock:query");
+        mock.expectedMessageCount(1);
+
+        template.requestBodyAndHeader("direct:query", null, "num1", 5);
+
+        MockEndpoint.assertIsSatisfied(context);
+
+        Exchange exchange = mock.getExchanges().get(0);
+        assertEquals(Integer.valueOf(3), 
exchange.getIn().getBody(Map.class).get("resultofsub"));
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() {
+        return new RouteBuilder() {
+            @Override
+            public void configure() {
+                getContext().getComponent("sql-stored", 
SqlStoredComponent.class).setDataSource(db);
+
+                from("direct:query")
+                        .to("sql-stored:SUBNUMBERS(INTEGER 
${headers.num1},INTEGER ${val(2)},OUT INTEGER resultofsub)")
+                        .to("mock:query");
+            }
+        };
+    }
+
+}

Reply via email to