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

virajjasani pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/phoenix-adapters.git


The following commit(s) were added to refs/heads/main by this push:
     new 891efdb  Regex based replacement of expression attribute names
891efdb is described below

commit 891efdb7b7c243b6aa10805a3983a5e27c50168c
Author: Palash Chauhan <[email protected]>
AuthorDate: Fri Feb 20 16:52:35 2026 -0800

    Regex based replacement of expression attribute names
---
 .../phoenix/ddb/service/UpdateItemService.java     |  10 +-
 .../apache/phoenix/ddb/service/utils/DQLUtils.java |   6 +-
 .../apache/phoenix/ddb/CommonServiceUtilsTest.java | 256 +++++++++++++++++++++
 .../apache/phoenix/ddb/UpdateItemBaseTests.java    |  78 ++++++-
 .../phoenix/ddb/utils/CommonServiceUtils.java      |  29 ++-
 5 files changed, 368 insertions(+), 11 deletions(-)

diff --git 
a/phoenix-ddb-rest/src/main/java/org/apache/phoenix/ddb/service/UpdateItemService.java
 
b/phoenix-ddb-rest/src/main/java/org/apache/phoenix/ddb/service/UpdateItemService.java
index 20a587f..e478441 100644
--- 
a/phoenix-ddb-rest/src/main/java/org/apache/phoenix/ddb/service/UpdateItemService.java
+++ 
b/phoenix-ddb-rest/src/main/java/org/apache/phoenix/ddb/service/UpdateItemService.java
@@ -7,6 +7,7 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
+import org.apache.phoenix.ddb.service.exceptions.ValidationException;
 import 
org.apache.phoenix.expression.util.bson.BsonUpdateInvalidArgumentException;
 import org.apache.phoenix.expression.util.bson.UpdateExpressionUtils;
 import org.bson.BsonDocument;
@@ -130,8 +131,13 @@ public class UpdateItemService {
                 (Map<String, Object>) 
request.get(ApiMetadata.ATTRIBUTE_UPDATES);
         BsonDocument updateDoc;
         if (updateExpression != null) {
-            updateDoc = 
CommonServiceUtils.getBsonUpdateExpressionFromMap(updateExpression,
-                    exprAttrNames, exprAttrVals);
+            try {
+                updateDoc = 
CommonServiceUtils.getBsonUpdateExpressionFromMap(updateExpression,
+                        exprAttrNames, exprAttrVals);
+            } catch (IllegalArgumentException e) {
+                throw new ValidationException(e.getMessage());
+            }
+
         } else {
             updateDoc = 
CommonServiceUtils.getBsonUpdateExpressionFromAttributeUpdates(
                     attributeUpdates);
diff --git 
a/phoenix-ddb-rest/src/main/java/org/apache/phoenix/ddb/service/utils/DQLUtils.java
 
b/phoenix-ddb-rest/src/main/java/org/apache/phoenix/ddb/service/utils/DQLUtils.java
index 0c7987f..15aa172 100644
--- 
a/phoenix-ddb-rest/src/main/java/org/apache/phoenix/ddb/service/utils/DQLUtils.java
+++ 
b/phoenix-ddb-rest/src/main/java/org/apache/phoenix/ddb/service/utils/DQLUtils.java
@@ -131,7 +131,11 @@ public class DQLUtils {
             return null;
         }
         List<String> projectionList = new ArrayList<>();
-        projExpr = 
CommonServiceUtils.replaceExpressionAttributeNames(projExpr, exprAttrNames);
+        try {
+            projExpr = 
CommonServiceUtils.replaceExpressionAttributeNames(projExpr, exprAttrNames);
+        } catch (IllegalArgumentException e) {
+            throw new ValidationException(e.getMessage());
+        }
         String[] projectionArray = projExpr.split("\\s*,\\s*");
         projectionList.addAll(Arrays.asList(projectionArray));
         return projectionList;
diff --git 
a/phoenix-ddb-rest/src/test/java/org/apache/phoenix/ddb/CommonServiceUtilsTest.java
 
b/phoenix-ddb-rest/src/test/java/org/apache/phoenix/ddb/CommonServiceUtilsTest.java
new file mode 100644
index 0000000..963d32a
--- /dev/null
+++ 
b/phoenix-ddb-rest/src/test/java/org/apache/phoenix/ddb/CommonServiceUtilsTest.java
@@ -0,0 +1,256 @@
+/*
+ * 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.phoenix.ddb;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.phoenix.ddb.utils.CommonServiceUtils;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class CommonServiceUtilsTest {
+
+    @Test
+    public void testReplaceExpressionAttributeNames_nullMap_noPlaceholders() {
+        String input = "SET name = :1";
+        String result = 
CommonServiceUtils.replaceExpressionAttributeNames(input, null);
+        Assert.assertEquals(input, result);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void 
testReplaceExpressionAttributeNames_nullMap_withPlaceholders_throwsException() {
+        String input = "SET #1 = :1";
+        CommonServiceUtils.replaceExpressionAttributeNames(input, null);
+    }
+
+    @Test
+    public void testReplaceExpressionAttributeNames_emptyMap_noPlaceholders() {
+        String input = "SET name = :1";
+        Map<String, String> exprAttrNames = new HashMap<>();
+        String result = 
CommonServiceUtils.replaceExpressionAttributeNames(input, exprAttrNames);
+        Assert.assertEquals(input, result);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void 
testReplaceExpressionAttributeNames_emptyMap_withPlaceholders_throwsException() 
{
+        String input = "SET #1 = :1";
+        Map<String, String> exprAttrNames = new HashMap<>();
+        CommonServiceUtils.replaceExpressionAttributeNames(input, 
exprAttrNames);
+    }
+
+    @Test
+    public void 
testReplaceExpressionAttributeNames_nullOrEmptyMap_exceptionMessage() {
+        String input = "SET #myAttr = :1";
+        try {
+            CommonServiceUtils.replaceExpressionAttributeNames(input, null);
+            Assert.fail("Expected IllegalArgumentException");
+        } catch (IllegalArgumentException e) {
+            Assert.assertTrue(e.getMessage().contains("#myAttr"));
+            Assert.assertTrue(e.getMessage().contains("not provided"));
+        }
+    }
+
+    @Test
+    public void testReplaceExpressionAttributeNames_noHashInString() {
+        String input = "SET name = :1";
+        Map<String, String> exprAttrNames = new HashMap<>();
+        exprAttrNames.put("#1", "attr1");
+        String result = 
CommonServiceUtils.replaceExpressionAttributeNames(input, exprAttrNames);
+        Assert.assertEquals(input, result);
+    }
+
+    @Test
+    public void testReplaceExpressionAttributeNames_simpleReplacement() {
+        String input = "SET #1 = :1";
+        Map<String, String> exprAttrNames = new HashMap<>();
+        exprAttrNames.put("#1", "myAttribute");
+        String result = 
CommonServiceUtils.replaceExpressionAttributeNames(input, exprAttrNames);
+        Assert.assertEquals("SET myAttribute = :1", result);
+    }
+
+    @Test
+    public void testReplaceExpressionAttributeNames_multipleReplacements() {
+        String input = "SET #1 = :1, #2 = :2, #3 = :3";
+        Map<String, String> exprAttrNames = new HashMap<>();
+        exprAttrNames.put("#1", "attr1");
+        exprAttrNames.put("#2", "attr2");
+        exprAttrNames.put("#3", "attr3");
+        String result = 
CommonServiceUtils.replaceExpressionAttributeNames(input, exprAttrNames);
+        Assert.assertEquals("SET attr1 = :1, attr2 = :2, attr3 = :3", result);
+    }
+
+    @Test
+    public void 
testReplaceExpressionAttributeNames_substringIssue_hash1AndHash10() {
+        // This is the key test case - #1 should not be replaced within #10
+        String input = "SET #1 = :1, #10 = :10";
+        Map<String, String> exprAttrNames = new HashMap<>();
+        exprAttrNames.put("#1", "sk");
+        exprAttrNames.put("#10", "task_counters");
+        String result = 
CommonServiceUtils.replaceExpressionAttributeNames(input, exprAttrNames);
+        Assert.assertEquals("SET sk = :1, task_counters = :10", result);
+    }
+
+    @Test
+    public void 
testReplaceExpressionAttributeNames_substringIssue_hash1AndHash11AndHash111() {
+        String input = "SET #1 = :1, #11 = :11, #111 = :111";
+        Map<String, String> exprAttrNames = new HashMap<>();
+        exprAttrNames.put("#1", "a");
+        exprAttrNames.put("#11", "b");
+        exprAttrNames.put("#111", "c");
+        String result = 
CommonServiceUtils.replaceExpressionAttributeNames(input, exprAttrNames);
+        Assert.assertEquals("SET a = :1, b = :11, c = :111", result);
+    }
+
+    @Test
+    public void testReplaceExpressionAttributeNames_realTest() {
+        String input = "SET #2 = :2, #3 = :3, #10 = :4, #4 = :5, #5 = :6 
REMOVE #9, #7";
+        Map<String, String> exprAttrNames = new HashMap<>();
+        exprAttrNames.put("#0", "hk");
+        exprAttrNames.put("#1", "sk");
+        exprAttrNames.put("#2", "worker_id");
+        exprAttrNames.put("#3", "last_evaluated_key");
+        exprAttrNames.put("#4", "execute_after");
+        exprAttrNames.put("#5", "scheduled_execute_after");
+        exprAttrNames.put("#6", "execution_number");
+        exprAttrNames.put("#7", "ready_for_post_hook");
+        exprAttrNames.put("#8", "ExpiresAtEpochSec");
+        exprAttrNames.put("#9", "^");
+        exprAttrNames.put("#10", "task_counters");
+        
+        String result = 
CommonServiceUtils.replaceExpressionAttributeNames(input, exprAttrNames);
+        String expected = "SET worker_id = :2, last_evaluated_key = :3, 
task_counters = :4, " +
+                "execute_after = :5, scheduled_execute_after = :6 REMOVE ^, 
ready_for_post_hook";
+        Assert.assertEquals(expected, result);
+    }
+
+    @Test
+    public void testReplaceExpressionAttributeNames_alphanumericNames() {
+        String input = "SET #name = :1, #my_attr = :2";
+        Map<String, String> exprAttrNames = new HashMap<>();
+        exprAttrNames.put("#name", "firstName");
+        exprAttrNames.put("#my_attr", "lastName");
+        String result = 
CommonServiceUtils.replaceExpressionAttributeNames(input, exprAttrNames);
+        Assert.assertEquals("SET firstName = :1, lastName = :2", result);
+    }
+
+    @Test
+    public void 
testReplaceExpressionAttributeNames_mixedNumericAndAlphanumeric() {
+        String input = "SET #1 = :1, #name = :2, #10 = :3";
+        Map<String, String> exprAttrNames = new HashMap<>();
+        exprAttrNames.put("#1", "id");
+        exprAttrNames.put("#name", "userName");
+        exprAttrNames.put("#10", "score");
+        String result = 
CommonServiceUtils.replaceExpressionAttributeNames(input, exprAttrNames);
+        Assert.assertEquals("SET id = :1, userName = :2, score = :3", result);
+    }
+
+    @Test
+    public void testReplaceExpressionAttributeNames_nestedAttributes() {
+        String input = "SET #1.#2 = :1";
+        Map<String, String> exprAttrNames = new HashMap<>();
+        exprAttrNames.put("#1", "parent");
+        exprAttrNames.put("#2", "child");
+        String result = 
CommonServiceUtils.replaceExpressionAttributeNames(input, exprAttrNames);
+        Assert.assertEquals("SET parent.child = :1", result);
+    }
+
+    @Test
+    public void testReplaceExpressionAttributeNames_listIndex() {
+        String input = "SET #1[0] = :1, #2[1].#3 = :2";
+        Map<String, String> exprAttrNames = new HashMap<>();
+        exprAttrNames.put("#1", "myList");
+        exprAttrNames.put("#2", "nestedList");
+        exprAttrNames.put("#3", "attr");
+        String result = 
CommonServiceUtils.replaceExpressionAttributeNames(input, exprAttrNames);
+        Assert.assertEquals("SET myList[0] = :1, nestedList[1].attr = :2", 
result);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void 
testReplaceExpressionAttributeNames_unmatchedPlaceholder_throwsException() {
+        String input = "SET #1 = :1, #2 = :2";
+        Map<String, String> exprAttrNames = new HashMap<>();
+        exprAttrNames.put("#1", "attr1");
+        // #2 is not in the map - should throw exception
+        CommonServiceUtils.replaceExpressionAttributeNames(input, 
exprAttrNames);
+    }
+
+    @Test
+    public void 
testReplaceExpressionAttributeNames_unmatchedPlaceholder_exceptionMessage() {
+        String input = "SET #1 = :1, #missing = :2";
+        Map<String, String> exprAttrNames = new HashMap<>();
+        exprAttrNames.put("#1", "attr1");
+        try {
+            CommonServiceUtils.replaceExpressionAttributeNames(input, 
exprAttrNames);
+            Assert.fail("Expected IllegalArgumentException");
+        } catch (IllegalArgumentException e) {
+            Assert.assertTrue(e.getMessage().contains("#missing"));
+            
Assert.assertTrue(e.getMessage().contains("ExpressionAttributeNames"));
+        }
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void 
testReplaceExpressionAttributeNames_onlyPlaceholderMissing_throwsException() {
+        String input = "SET #1 = :1";
+        Map<String, String> exprAttrNames = new HashMap<>();
+        exprAttrNames.put("#other", "other_attr");
+        // #1 is not in the map, but map is not empty so validation happens
+        CommonServiceUtils.replaceExpressionAttributeNames(input, 
exprAttrNames);
+    }
+
+    @Test
+    public void testReplaceExpressionAttributeNames_conditionExpression() {
+        String input = "attribute_exists(#0) AND attribute_exists(#1) AND #4 = 
:0 AND #6 = :1";
+        Map<String, String> exprAttrNames = new HashMap<>();
+        exprAttrNames.put("#0", "hk");
+        exprAttrNames.put("#1", "sk");
+        exprAttrNames.put("#4", "execute_after");
+        exprAttrNames.put("#6", "execution_number");
+        String result = 
CommonServiceUtils.replaceExpressionAttributeNames(input, exprAttrNames);
+        Assert.assertEquals("attribute_exists(hk) AND attribute_exists(sk) AND 
execute_after = :0 AND execution_number = :1", result);
+    }
+
+    @Test
+    public void testReplaceExpressionAttributeNames_underscoreInPlaceholder() {
+        String input = "SET #my_attr_1 = :1, #my_attr_10 = :2";
+        Map<String, String> exprAttrNames = new HashMap<>();
+        exprAttrNames.put("#my_attr_1", "first");
+        exprAttrNames.put("#my_attr_10", "tenth");
+        String result = 
CommonServiceUtils.replaceExpressionAttributeNames(input, exprAttrNames);
+        Assert.assertEquals("SET first = :1, tenth = :2", result);
+    }
+
+    @Test
+    public void testReplaceExpressionAttributeNames_emptyString() {
+        String input = "";
+        Map<String, String> exprAttrNames = new HashMap<>();
+        exprAttrNames.put("#1", "attr1");
+        String result = 
CommonServiceUtils.replaceExpressionAttributeNames(input, exprAttrNames);
+        Assert.assertEquals("", result);
+    }
+
+    @Test
+    public void testReplaceExpressionAttributeNames_adjacentPlaceholders() {
+        String input = "#1#2";
+        Map<String, String> exprAttrNames = new HashMap<>();
+        exprAttrNames.put("#1", "a");
+        exprAttrNames.put("#2", "b");
+        String result = 
CommonServiceUtils.replaceExpressionAttributeNames(input, exprAttrNames);
+        Assert.assertEquals("ab", result);
+    }
+}
diff --git 
a/phoenix-ddb-rest/src/test/java/org/apache/phoenix/ddb/UpdateItemBaseTests.java
 
b/phoenix-ddb-rest/src/test/java/org/apache/phoenix/ddb/UpdateItemBaseTests.java
index 6e650ac..cf06696 100644
--- 
a/phoenix-ddb-rest/src/test/java/org/apache/phoenix/ddb/UpdateItemBaseTests.java
+++ 
b/phoenix-ddb-rest/src/test/java/org/apache/phoenix/ddb/UpdateItemBaseTests.java
@@ -602,7 +602,7 @@ public class UpdateItemBaseTests {
         GetItemRequest gir = 
GetItemRequest.builder().tableName(tableName).key(key).build();
         GetItemResponse phoenixResult = phoenixDBClientV2.getItem(gir);
         GetItemResponse dynamoResult = dynamoDbClient.getItem(gir);
-        Assert.assertEquals(dynamoResult.item(), phoenixResult.item());
+        ItemComparator.areItemsEqual(dynamoResult.item(), 
phoenixResult.item());
     }
 
     protected Map<String, AttributeValue> getItem1() {
@@ -1715,4 +1715,80 @@ public class UpdateItemBaseTests {
             key.put("PK2", AttributeValue.builder().n("1").build());
         return key;
     }
+
+    @Test(timeout = 120000)
+    public void testSetAddRemoveWithOverlappingPlaceholders1() {
+        final String tableName = 
testName.getMethodName().replaceAll("[\\[\\]]", "");
+        createTableAndPutItem(tableName, true);
+
+        Map<String, AttributeValue> key = getKey();
+        UpdateItemRequest.Builder uir = 
UpdateItemRequest.builder().tableName(tableName).key(key);
+        uir.updateExpression(
+            "SET #my_col = :1, #my_col1 = :2, #my_col10 = :3, #c = #c + :4, 
#c_1 = :5 " +
+            "ADD #top_set :6 " +
+            "REMOVE #rm_attr, #rm_attr1");
+
+        Map<String, String> exprAttrNames = new HashMap<>();
+        exprAttrNames.put("#my_col", "COL2");
+        exprAttrNames.put("#my_col1", "COL3");
+        exprAttrNames.put("#my_col10", "NewField10");
+        exprAttrNames.put("#c", "COL1");
+        exprAttrNames.put("#c_1", "COL4");
+        exprAttrNames.put("#top_set", "TopLevelSet");
+        exprAttrNames.put("#rm_attr", "Counter");
+        exprAttrNames.put("#rm_attr1", "Reviews");
+        uir.expressionAttributeNames(exprAttrNames);
+
+        Map<String, AttributeValue> exprAttrVal = new HashMap<>();
+        exprAttrVal.put(":1", 
AttributeValue.builder().s("UpdatedTitle").build());
+        exprAttrVal.put(":2", 
AttributeValue.builder().s("UpdatedDesc").build());
+        exprAttrVal.put(":3", AttributeValue.builder().n("999").build());
+        exprAttrVal.put(":4", AttributeValue.builder().n("5").build());
+        exprAttrVal.put(":5", AttributeValue.builder().n("100").build());
+        exprAttrVal.put(":6", 
AttributeValue.builder().ss("newMember").build());
+        uir.expressionAttributeValues(exprAttrVal);
+        uir.returnValues(ALL_NEW);
+
+        UpdateItemResponse dynamoResult = 
dynamoDbClient.updateItem(uir.build());
+        UpdateItemResponse phoenixResult = 
phoenixDBClientV2.updateItem(uir.build());
+        ItemComparator.areItemsEqual(dynamoResult.attributes(), 
phoenixResult.attributes());
+
+        validateItem(tableName, key);
+    }
+
+    @Test(timeout = 120000)
+    public void testSetAddRemoveWithOverlappingPlaceholders2() {
+        final String tableName = 
testName.getMethodName().replaceAll("[\\[\\]]", "");
+        createTableAndPutItem(tableName, true);
+
+        Map<String, AttributeValue> key = getKey();
+        UpdateItemRequest.Builder uir = 
UpdateItemRequest.builder().tableName(tableName).key(key);
+        uir.updateExpression(
+            "SET #1 = #1 + :1, #11 = if_not_exists(#11, :11), #111 = :111 " +
+            "ADD #3 :3 " +
+            "REMOVE #4, #5");
+
+        Map<String, String> exprAttrNames = new HashMap<>();
+        exprAttrNames.put("#1", "COL1");
+        exprAttrNames.put("#11", "NewCol11");
+        exprAttrNames.put("#111", "NewCol111");
+        exprAttrNames.put("#3", "TopLevelSet");
+        exprAttrNames.put("#4", "COL3");
+        exprAttrNames.put("#5", "Reviews");
+        uir.expressionAttributeNames(exprAttrNames);
+
+        Map<String, AttributeValue> exprAttrVal = new HashMap<>();
+        exprAttrVal.put(":1", AttributeValue.builder().n("10").build());
+        exprAttrVal.put(":11", 
AttributeValue.builder().s("default11").build());
+        exprAttrVal.put(":111", 
AttributeValue.builder().s("value111").build());
+        exprAttrVal.put(":3", 
AttributeValue.builder().ss("addedMember").build());
+        uir.expressionAttributeValues(exprAttrVal);
+        uir.returnValues(ALL_NEW);
+
+        UpdateItemResponse dynamoResult = 
dynamoDbClient.updateItem(uir.build());
+        UpdateItemResponse phoenixResult = 
phoenixDBClientV2.updateItem(uir.build());
+        ItemComparator.areItemsEqual(dynamoResult.attributes(), 
phoenixResult.attributes());
+
+        validateItem(tableName, key);
+    }
 }
diff --git 
a/phoenix-ddb-utils/src/main/java/org/apache/phoenix/ddb/utils/CommonServiceUtils.java
 
b/phoenix-ddb-utils/src/main/java/org/apache/phoenix/ddb/utils/CommonServiceUtils.java
index 64b81a5..f7f7684 100644
--- 
a/phoenix-ddb-utils/src/main/java/org/apache/phoenix/ddb/utils/CommonServiceUtils.java
+++ 
b/phoenix-ddb-utils/src/main/java/org/apache/phoenix/ddb/utils/CommonServiceUtils.java
@@ -38,6 +38,8 @@ import java.util.Map;
 import java.util.HashMap;
 import java.util.List;
 import java.util.ArrayList;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 /**
  * Common utilities to be used by phoenixDBClientV2 APIs.
@@ -46,6 +48,7 @@ public class CommonServiceUtils {
 
     public static final String DOUBLE_QUOTE = "\"";
     public static final String HASH = "#";
+    private static final Pattern EXPR_ATTR_NAME_PATTERN = 
Pattern.compile("#([a-zA-Z0-9_]+)");
 
     public static boolean isCauseMessageAvailable(Exception e) {
         return e.getCause() != null && e.getCause().getMessage() != null;
@@ -158,16 +161,28 @@ public class CommonServiceUtils {
 
     /**
      * Replace all occurrences of aliases in the given String using expression 
attribute map.
+     * @throws IllegalArgumentException if a placeholder is found that is not 
in the map,
+     *         or if the expression contains placeholders but the map is 
null/empty
      */
     public static String replaceExpressionAttributeNames(String s,
-            Map<String, String> exprAttrNames) {
-        if (exprAttrNames == null || !s.contains(HASH)) {
-            return s;
-        }
-        for (String k : exprAttrNames.keySet()) {
-            s = StringUtils.replace(s, k, exprAttrNames.get(k));
+                                                         Map<String, String> 
exprAttrNames) {
+        Matcher m = EXPR_ATTR_NAME_PATTERN.matcher(s);
+        StringBuffer result = new StringBuffer();
+        while (m.find()) {
+            String key = m.group(0);
+            if (exprAttrNames == null || exprAttrNames.isEmpty()) {
+                throw new IllegalArgumentException(
+                    "Expression contains placeholder " + key + " but 
ExpressionAttributeNames is not provided");
+            }
+            String replacement = exprAttrNames.get(key);
+            if (replacement == null) {
+                throw new IllegalArgumentException(
+                    "Expression attribute name " + key + " not found in 
ExpressionAttributeNames");
+            }
+            m.appendReplacement(result, Matcher.quoteReplacement(replacement));
         }
-        return s;
+        m.appendTail(result);
+        return result.toString();
     }
 
     public static Map<String, Object> getConsumedCapacity(final String 
tableName) {

Reply via email to