[ASTERIXDB-2419][COMP] Replacement limit in regexp_replace()

- user model changes: yes
- storage format changes: no
- interface changes: no

Details:
- Support replacement limit parameter in regexp_replace()

Change-Id: I9bcf633ea6af1ade49daffec4784a70355c08a0e
Reviewed-on: https://asterix-gerrit.ics.uci.edu/2797
Tested-by: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Contrib: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Integration-Tests: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Reviewed-by: Till Westmann <tillw@apache.org>
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/regexp_replace/regexp_replace.6.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/regexp_replace/regexp_replace.6.query.sqlpp
new file mode 100644
index 0000000..94deadc
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/regexp_replace/regexp_replace.6.query.sqlpp
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ */
+
+{
+  'result1':regexp_replace('abracadabra','bra','kkk',-1),
+  'result2':regexp_replace('abracadabra','bra','kkk',0),
+  'result3':regexp_replace('abracadabra','bra','kkk',1),
+  'result4':regexp_replace('abracadabra_abracadabra','bra','kkk',2),
+  'result5':regexp_replace('abracadabra_abracadabra','bra','kkk',5)
+};
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/string/regexp_replace/regexp_replace.6.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/string/regexp_replace/regexp_replace.6.adm
new file mode 100644
index 0000000..6fc838d
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/string/regexp_replace/regexp_replace.6.adm
@@ -0,0 +1 @@
+{ "result1": "abracadabra", "result2": "abracadabra", "result3": "akkkcadabra", "result4": "akkkcadakkk_abracadabra", "result5": "akkkcadakkk_akkkcadakkk" }
\ No newline at end of file
diff --git a/asterixdb/asterix-doc/src/main/markdown/builtins/2_string_common.md b/asterixdb/asterix-doc/src/main/markdown/builtins/2_string_common.md
index 6599dc3..1f9ac19 100644
--- a/asterixdb/asterix-doc/src/main/markdown/builtins/2_string_common.md
+++ b/asterixdb/asterix-doc/src/main/markdown/builtins/2_string_common.md
@@ -329,6 +329,7 @@
  * Syntax:
 
         regexp_replace(string, string_pattern, string_replacement[, string_flags])
+        regexp_replace(string, string_pattern, string_replacement[, replacement_limit])
 
  * Checks whether the string `string` matches the given
    regular expression pattern `string_pattern` (a Java regular expression pattern),
@@ -339,6 +340,7 @@
     * `string_replacement` : a pattern `string` to be used as the replacement,
     * `string_flag` : (Optional) a `string` with flags to be used during replace.
         * The following modes are enabled with these flags: dotall (s), multiline (m), case_insensitive (i), and comments and whitespace (x).
+    * `replacement_limit`: (Optional) an `integer` specifying the maximum number of replacements to make
  * Return Value:
     * Returns a `string` that is obtained after the replacements,
     * `missing` if any argument is a `missing` value,
diff --git a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/functions/BuiltinFunctions.java b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/functions/BuiltinFunctions.java
index 8f24864..4afeef1 100644
--- a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/functions/BuiltinFunctions.java
+++ b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/functions/BuiltinFunctions.java
@@ -1273,7 +1273,8 @@
         addFunction(STRING_REGEXP_POSITION, StringInt32TypeComputer.INSTANCE, true);
         addFunction(STRING_REGEXP_POSITION_WITH_FLAG, StringInt32TypeComputer.INSTANCE, true);
         addFunction(STRING_REGEXP_REPLACE, StringStringTypeComputer.INSTANCE, true);
-        addFunction(STRING_REGEXP_REPLACE_WITH_FLAG, StringStringTypeComputer.INSTANCE, true);
+        addFunction(STRING_REGEXP_REPLACE_WITH_FLAG,
+                StringIntToStringTypeComputer.INSTANCE_STRING_REGEXP_REPLACE_WITH_FLAG, true);
         addFunction(STRING_REPLACE, StringStringTypeComputer.INSTANCE, true);
         addFunction(STRING_REPLACE_WITH_LIMIT, StringIntToStringTypeComputer.INSTANCE_TRIPLE_STRING, true);
         addFunction(STRING_REVERSE, StringStringTypeComputer.INSTANCE, true);
diff --git a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/StringIntToStringTypeComputer.java b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/StringIntToStringTypeComputer.java
index 466ba4d..0f376ba 100644
--- a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/StringIntToStringTypeComputer.java
+++ b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/StringIntToStringTypeComputer.java
@@ -18,6 +18,9 @@
  */
 package org.apache.asterix.om.typecomputer.impl;
 
+import java.util.EnumSet;
+import java.util.Set;
+
 import org.apache.asterix.om.exceptions.TypeMismatchException;
 import org.apache.asterix.om.typecomputer.base.AbstractResultTypeComputer;
 import org.apache.asterix.om.types.ATypeTag;
@@ -28,40 +31,73 @@
 import org.apache.hyracks.api.exceptions.SourceLocation;
 
 public class StringIntToStringTypeComputer extends AbstractResultTypeComputer {
-    public static final StringIntToStringTypeComputer INSTANCE = new StringIntToStringTypeComputer(1);
+    public static final StringIntToStringTypeComputer INSTANCE = new StringIntToStringTypeComputer(0, 0, 1, 1);
 
-    public static final StringIntToStringTypeComputer INSTANCE_TRIPLE_STRING = new StringIntToStringTypeComputer(3);
+    public static final StringIntToStringTypeComputer INSTANCE_TRIPLE_STRING =
+            new StringIntToStringTypeComputer(0, 2, 3, 3);
 
-    private final int stringArgCount;
+    public static final StringIntToStringTypeComputer INSTANCE_STRING_REGEXP_REPLACE_WITH_FLAG =
+            new StringIntToStringTypeComputer(0, 3, 3, 3);
 
-    public StringIntToStringTypeComputer(int stringArgCount) {
-        this.stringArgCount = stringArgCount;
+    private final int stringArgIdxMin;
+
+    private final int stringArgIdxMax;
+
+    private final int intArgIdxMin;
+
+    private final int intArgIdxMax;
+
+    public StringIntToStringTypeComputer(int stringArgIdxMin, int stringArgIdxMax, int intArgIdxMin, int intArgIdxMax) {
+        this.stringArgIdxMin = stringArgIdxMin;
+        this.stringArgIdxMax = stringArgIdxMax;
+        this.intArgIdxMin = intArgIdxMin;
+        this.intArgIdxMax = intArgIdxMax;
     }
 
     @Override
     public void checkArgType(String funcName, int argIndex, IAType type, SourceLocation sourceLoc)
             throws AlgebricksException {
         ATypeTag tag = type.getTypeTag();
-        if (argIndex < stringArgCount) {
-            if (tag != ATypeTag.STRING) {
-                throw new TypeMismatchException(sourceLoc, funcName, argIndex, tag, ATypeTag.STRING);
+        boolean expectedStringType = false;
+        if (stringArgIdxMin <= argIndex && argIndex <= stringArgIdxMax) {
+            if (tag == ATypeTag.STRING) {
+                return;
             }
-        } else {
+            expectedStringType = true;
+        }
+
+        boolean expectedIntType = false;
+        if (intArgIdxMin <= argIndex && argIndex <= intArgIdxMax) {
             switch (tag) {
                 case TINYINT:
                 case SMALLINT:
                 case INTEGER:
                 case BIGINT:
-                    break;
-                default:
-                    throw new TypeMismatchException(sourceLoc, funcName, argIndex, tag, ATypeTag.TINYINT,
-                            ATypeTag.SMALLINT, ATypeTag.INTEGER, ATypeTag.BIGINT);
+                    return;
             }
+            expectedIntType = true;
         }
+
+        throw new TypeMismatchException(sourceLoc, funcName, argIndex, tag,
+                getExpectedTypes(expectedStringType, expectedIntType));
     }
 
     @Override
     public IAType getResultType(ILogicalExpression expr, IAType... types) throws AlgebricksException {
         return BuiltinType.ASTRING;
     }
+
+    private ATypeTag[] getExpectedTypes(boolean expectedStringType, boolean expectedIntType) {
+        Set<ATypeTag> expectedTypes = EnumSet.noneOf(ATypeTag.class);
+        if (expectedStringType) {
+            expectedTypes.add(ATypeTag.STRING);
+        }
+        if (expectedIntType) {
+            expectedTypes.add(ATypeTag.TINYINT);
+            expectedTypes.add(ATypeTag.SMALLINT);
+            expectedTypes.add(ATypeTag.INTEGER);
+            expectedTypes.add(ATypeTag.BIGINT);
+        }
+        return expectedTypes.toArray(new ATypeTag[0]);
+    }
 }
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/AbstractQuadStringStringEval.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/AbstractQuadStringStringEval.java
index 5de007b..8a66e46 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/AbstractQuadStringStringEval.java
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/AbstractQuadStringStringEval.java
@@ -47,16 +47,16 @@
 
     private ArrayBackedValueStorage resultStorage = new ArrayBackedValueStorage();
     private DataOutput dout = resultStorage.getDataOutput();
-    private IPointable array0 = new VoidPointable();
-    private IPointable array1 = new VoidPointable();
-    private IPointable array2 = new VoidPointable();
-    private IPointable array3 = new VoidPointable();
+    private IPointable ptr0 = new VoidPointable();
+    private IPointable ptr1 = new VoidPointable();
+    private IPointable ptr2 = new VoidPointable();
+    private IPointable ptr3 = new VoidPointable();
     private IScalarEvaluator eval0;
     private IScalarEvaluator eval1;
     private IScalarEvaluator eval2;
     private IScalarEvaluator eval3;
 
-    private final FunctionIdentifier funcID;
+    protected final FunctionIdentifier funcID;
     protected final SourceLocation sourceLoc;
 
     private AMutableString resultBuffer = new AMutableString("");
@@ -64,10 +64,10 @@
     private ISerializerDeserializer strSerde =
             SerializerDeserializerProvider.INSTANCE.getSerializerDeserializer(BuiltinType.ASTRING);
 
-    private final UTF8StringPointable strPtr1st = new UTF8StringPointable();
-    private final UTF8StringPointable strPtr2nd = new UTF8StringPointable();
-    private final UTF8StringPointable strPtr3rd = new UTF8StringPointable();
-    private final UTF8StringPointable strPtr4th = new UTF8StringPointable();
+    private final UTF8StringPointable strPtr0 = new UTF8StringPointable();
+    private final UTF8StringPointable strPtr1 = new UTF8StringPointable();
+    private final UTF8StringPointable strPtr2 = new UTF8StringPointable();
+    private final UTF8StringPointable strPtr3 = new UTF8StringPointable();
 
     public AbstractQuadStringStringEval(IHyracksTaskContext context, IScalarEvaluatorFactory eval0,
             IScalarEvaluatorFactory eval1, IScalarEvaluatorFactory eval2, IScalarEvaluatorFactory eval3,
@@ -83,48 +83,19 @@
     @SuppressWarnings("unchecked")
     @Override
     public void evaluate(IFrameTupleReference tuple, IPointable result) throws HyracksDataException {
-        eval0.evaluate(tuple, array0);
-        eval1.evaluate(tuple, array1);
-        eval2.evaluate(tuple, array2);
-        eval3.evaluate(tuple, array3);
+        eval0.evaluate(tuple, ptr0);
+        eval1.evaluate(tuple, ptr1);
+        eval2.evaluate(tuple, ptr2);
+        eval3.evaluate(tuple, ptr3);
 
-        byte[] bytes0 = array0.getByteArray();
-        byte[] bytes1 = array1.getByteArray();
-        byte[] bytes2 = array2.getByteArray();
-        byte[] bytes3 = array3.getByteArray();
-
-        int start0 = array0.getStartOffset();
-        int start1 = array1.getStartOffset();
-        int start2 = array2.getStartOffset();
-        int start3 = array3.getStartOffset();
-
-        int len0 = array0.getLength();
-        int len1 = array1.getLength();
-        int len2 = array2.getLength();
-        int len3 = array3.getLength();
+        processArgument(0, ptr0, strPtr0);
+        processArgument(1, ptr1, strPtr1);
+        processArgument(2, ptr2, strPtr2);
+        processArgument(3, ptr3, strPtr3);
 
         resultStorage.reset();
-        // Type check.
-        if (bytes0[start0] != ATypeTag.SERIALIZED_STRING_TYPE_TAG) {
-            throw new TypeMismatchException(sourceLoc, funcID, 0, bytes0[start0], ATypeTag.SERIALIZED_STRING_TYPE_TAG);
-        }
-        if (bytes1[start1] != ATypeTag.SERIALIZED_STRING_TYPE_TAG) {
-            throw new TypeMismatchException(sourceLoc, funcID, 1, bytes1[start1], ATypeTag.SERIALIZED_STRING_TYPE_TAG);
-        }
-        if (bytes2[start2] != ATypeTag.SERIALIZED_STRING_TYPE_TAG) {
-            throw new TypeMismatchException(sourceLoc, funcID, 2, bytes2[start2], ATypeTag.SERIALIZED_STRING_TYPE_TAG);
-        }
-        if (bytes3[start3] != ATypeTag.SERIALIZED_STRING_TYPE_TAG) {
-            throw new TypeMismatchException(sourceLoc, funcID, 3, bytes1[start3], ATypeTag.SERIALIZED_STRING_TYPE_TAG);
-        }
-
-        strPtr1st.set(bytes0, start0 + 1, len0);
-        strPtr2nd.set(bytes1, start1 + 1, len1);
-        strPtr3rd.set(bytes2, start2 + 1, len2);
-        strPtr4th.set(bytes3, start3 + 1, len3);
-
         try {
-            String res = compute(strPtr1st, strPtr2nd, strPtr3rd, strPtr4th);
+            String res = compute(strPtr0, strPtr1, strPtr2, strPtr3);
             resultBuffer.setValue(res);
             strSerde.serialize(resultBuffer, dout);
         } catch (IOException e) {
@@ -133,7 +104,19 @@
         result.set(resultStorage);
     }
 
+    protected void processArgument(int argIdx, IPointable argPtr, UTF8StringPointable outStrPtr)
+            throws HyracksDataException {
+        byte[] bytes = argPtr.getByteArray();
+        int start = argPtr.getStartOffset();
+        // Type check.
+        if (bytes[start] != ATypeTag.SERIALIZED_STRING_TYPE_TAG) {
+            throw new TypeMismatchException(sourceLoc, funcID, argIdx, bytes[start],
+                    ATypeTag.SERIALIZED_STRING_TYPE_TAG);
+        }
+        int len = argPtr.getLength();
+        outStrPtr.set(bytes, start + 1, len);
+    }
+
     protected abstract String compute(UTF8StringPointable strPtr1st, UTF8StringPointable strPtr2nd,
             UTF8StringPointable strPtr3rd, UTF8StringPointable strPtr4th) throws IOException;
-
 }
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/StringRegExpReplaceWithFlagsDescriptor.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/StringRegExpReplaceWithFlagDescriptor.java
similarity index 61%
rename from asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/StringRegExpReplaceWithFlagsDescriptor.java
rename to asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/StringRegExpReplaceWithFlagDescriptor.java
index d901a22..707865c 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/StringRegExpReplaceWithFlagsDescriptor.java
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/StringRegExpReplaceWithFlagDescriptor.java
@@ -23,6 +23,8 @@
 import org.apache.asterix.om.functions.BuiltinFunctions;
 import org.apache.asterix.om.functions.IFunctionDescriptor;
 import org.apache.asterix.om.functions.IFunctionDescriptorFactory;
+import org.apache.asterix.om.types.ATypeTag;
+import org.apache.asterix.om.types.hierachy.ATypeHierarchy;
 import org.apache.asterix.runtime.evaluators.base.AbstractScalarFunctionDynamicDescriptor;
 import org.apache.asterix.runtime.evaluators.functions.utils.RegExpMatcher;
 import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
@@ -30,15 +32,16 @@
 import org.apache.hyracks.algebricks.runtime.base.IScalarEvaluatorFactory;
 import org.apache.hyracks.api.context.IHyracksTaskContext;
 import org.apache.hyracks.api.exceptions.HyracksDataException;
+import org.apache.hyracks.data.std.api.IPointable;
 import org.apache.hyracks.data.std.primitive.UTF8StringPointable;
 
-public class StringRegExpReplaceWithFlagsDescriptor extends AbstractScalarFunctionDynamicDescriptor {
+public class StringRegExpReplaceWithFlagDescriptor extends AbstractScalarFunctionDynamicDescriptor {
 
     private static final long serialVersionUID = 1L;
     public static final IFunctionDescriptorFactory FACTORY = new IFunctionDescriptorFactory() {
         @Override
         public IFunctionDescriptor createFunctionDescriptor() {
-            return new StringRegExpReplaceWithFlagsDescriptor();
+            return new StringRegExpReplaceWithFlagDescriptor();
         }
     };
 
@@ -50,14 +53,40 @@
             @Override
             public IScalarEvaluator createScalarEvaluator(IHyracksTaskContext ctx) throws HyracksDataException {
                 return new AbstractQuadStringStringEval(ctx, args[0], args[1], args[2], args[3],
-                        StringRegExpReplaceWithFlagsDescriptor.this.getIdentifier(), sourceLoc) {
+                        StringRegExpReplaceWithFlagDescriptor.this.getIdentifier(), sourceLoc) {
+                    private final UTF8StringPointable emptyFlags = UTF8StringPointable.generateUTF8Pointable("");
                     private final RegExpMatcher matcher = new RegExpMatcher();
+                    private int limit;
+
+                    @Override
+                    protected void processArgument(int argIdx, IPointable argPtr, UTF8StringPointable outStrPtr)
+                            throws HyracksDataException {
+                        if (argIdx == 3) {
+                            byte[] bytes = argPtr.getByteArray();
+                            int start = argPtr.getStartOffset();
+                            ATypeTag tt = ATypeTag.VALUE_TYPE_MAPPING[bytes[start]];
+                            switch (tt) {
+                                case TINYINT:
+                                case SMALLINT:
+                                case INTEGER:
+                                case BIGINT:
+                                    limit = ATypeHierarchy.getIntegerValue(funcID.getName(), argIdx, bytes, start,
+                                            true);
+                                    outStrPtr.set(emptyFlags);
+                                    return;
+                                default:
+                                    limit = Integer.MAX_VALUE;
+                                    break;
+                            }
+                        }
+                        super.processArgument(argIdx, argPtr, outStrPtr);
+                    }
 
                     @Override
                     protected String compute(UTF8StringPointable srcPtr, UTF8StringPointable patternPtr,
                             UTF8StringPointable replacePtr, UTF8StringPointable flagsPtr) throws IOException {
                         matcher.build(srcPtr, patternPtr, flagsPtr);
-                        return matcher.replace(replacePtr);
+                        return matcher.replace(replacePtr, limit);
                     }
                 };
             }
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/utils/RegExpMatcher.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/utils/RegExpMatcher.java
index 8d238df..050eb2e 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/utils/RegExpMatcher.java
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/utils/RegExpMatcher.java
@@ -166,6 +166,20 @@
      * @return a new string with contained regular expressions replaced.
      */
     public String replace(UTF8StringPointable replaceStrPtr) {
+        return replace(replaceStrPtr, Integer.MAX_VALUE);
+    }
+
+    /**
+     * Replaces the appearances of a regular expression defined pattern in a source string with a given
+     * replacement string.
+     *
+     * @param replaceStrPtr
+     *            , the string for replacing the regular expression.
+     * @param replaceLimit
+     *            , the maximum number of replacements to make
+     * @return a new string with contained regular expressions replaced.
+     */
+    public String replace(UTF8StringPointable replaceStrPtr, int replaceLimit) {
         // Sets up a new replacement string if necessary.
         final boolean newReplace =
                 replaceStrPtr != null && (replaceStr == null || lastReplaceStrPtr.compareTo(replaceStrPtr) != 0);
@@ -176,7 +190,7 @@
 
         // Does the actual replacement.
         resultBuf.setLength(0);
-        while (matcher.find()) {
+        for (int i = 0; i < replaceLimit && matcher.find(); i++) {
             matcher.appendReplacement(resultBuf, replaceStr);
         }
         matcher.appendTail(resultBuf);
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/functions/FunctionCollection.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/functions/FunctionCollection.java
index 04f4a1e..6b0b5d5 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/functions/FunctionCollection.java
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/functions/FunctionCollection.java
@@ -257,7 +257,7 @@
 import org.apache.asterix.runtime.evaluators.functions.StringRegExpPositionDescriptor;
 import org.apache.asterix.runtime.evaluators.functions.StringRegExpPositionWithFlagDescriptor;
 import org.apache.asterix.runtime.evaluators.functions.StringRegExpReplaceDescriptor;
-import org.apache.asterix.runtime.evaluators.functions.StringRegExpReplaceWithFlagsDescriptor;
+import org.apache.asterix.runtime.evaluators.functions.StringRegExpReplaceWithFlagDescriptor;
 import org.apache.asterix.runtime.evaluators.functions.StringRepeatDescriptor;
 import org.apache.asterix.runtime.evaluators.functions.StringReplaceDescriptor;
 import org.apache.asterix.runtime.evaluators.functions.StringReplaceWithLimitDescriptor;
@@ -624,7 +624,7 @@
         fc.addGenerated(StringRegExpPositionDescriptor.FACTORY);
         fc.addGenerated(StringRegExpPositionWithFlagDescriptor.FACTORY);
         fc.addGenerated(StringRegExpReplaceDescriptor.FACTORY);
-        fc.addGenerated(StringRegExpReplaceWithFlagsDescriptor.FACTORY);
+        fc.addGenerated(StringRegExpReplaceWithFlagDescriptor.FACTORY);
         fc.addGenerated(StringInitCapDescriptor.FACTORY);
         fc.addGenerated(StringTrimDescriptor.FACTORY);
         fc.addGenerated(StringLTrimDescriptor.FACTORY);