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

mbudiu pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/calcite.git


The following commit(s) were added to refs/heads/main by this push:
     new 0c6a3c1fac [CALCITE-6310] Add REGEXP_REPLACE function (enabled in 
PostgreSQL library)
0c6a3c1fac is described below

commit 0c6a3c1face14cf8136a593ec25a7f13631a09df
Author: James Duong <[email protected]>
AuthorDate: Mon May 27 10:53:55 2024 -0700

    [CALCITE-6310] Add REGEXP_REPLACE function (enabled in PostgreSQL library)
    
    Reworked the overloading of REGEXP_REPLACE:
    * There is a two argument version that is only for Amazon Redshift, 
replacement string is the empty string
    * There is a three argument version that is for everything except PostgreSQL
    * PostgreSQL has its own 3 and 4 argument versions
    * PostgreSQL 3 argument version will only replace first occurrence
    * The g flag is now support for the 4 argument function in PostgreSQL
    * BigQuery only supports a 3 argument version REGEXP_REPLACE
    * BigQuery uses different syntax to refer to capture groups
    * There is a new REGEXP_REPLACE for BigQuery
    * No longer need to examine the ConformanceEnum to pick the correct 
implementation
    * MySQL and Amazon Redshift have two signatures with 5 arguments
    * Oracle only has one signature with 5 arguments
---
 babel/src/test/resources/sql/postgresql.iq         |  15 ++
 babel/src/test/resources/sql/redshift.iq           |  10 ++
 .../calcite/adapter/enumerable/RexImpTable.java    |  52 +++----
 .../org/apache/calcite/runtime/SqlFunctions.java   |  29 +++-
 .../calcite/sql/fun/SqlLibraryOperators.java       | 102 ++++++++++++-
 .../calcite/sql/fun/SqlRegexpReplaceFunction.java  |  75 ---------
 .../org/apache/calcite/sql/type/OperandTypes.java  |   4 +
 .../sql/validate/SqlAbstractConformance.java       |   4 -
 .../calcite/sql/validate/SqlConformance.java       |  16 --
 .../calcite/sql/validate/SqlConformanceEnum.java   |   9 --
 .../sql/validate/SqlDelegatingConformance.java     |   4 -
 .../org/apache/calcite/util/BuiltInMethod.java     |  12 +-
 .../apache/calcite/util/RelToSqlConverterUtil.java |   4 +-
 .../org/apache/calcite/test/SqlFunctionsTest.java  |   6 +
 .../org/apache/calcite/test/SqlValidatorTest.java  |  27 ++++
 site/_docs/reference.md                            |   2 +
 .../org/apache/calcite/test/SqlOperatorTest.java   | 169 +++++++++++++++++----
 17 files changed, 365 insertions(+), 175 deletions(-)

diff --git a/babel/src/test/resources/sql/postgresql.iq 
b/babel/src/test/resources/sql/postgresql.iq
index d2e0b53224..dc8a98d7ca 100644
--- a/babel/src/test/resources/sql/postgresql.iq
+++ b/babel/src/test/resources/sql/postgresql.iq
@@ -1231,4 +1231,19 @@ X
 1
 !ok
 
+SELECT regexp_replace('abc def GHI', '[a-z]+', 'X') AS x;
+X
+X def GHI
+!ok
+
+SELECT regexp_replace('abc def GHI', '[a-z]+', 'X', 'g') AS x;
+X
+X X GHI
+!ok
+
+SELECT regexp_replace('ABC def GHI', '[a-z]+', 'X', 'i') AS x;
+X
+X def GHI
+!ok
+
 # End postgresql.iq
diff --git a/babel/src/test/resources/sql/redshift.iq 
b/babel/src/test/resources/sql/redshift.iq
index ca0ed2fb9f..6104222746 100755
--- a/babel/src/test/resources/sql/redshift.iq
+++ b/babel/src/test/resources/sql/redshift.iq
@@ -2232,6 +2232,16 @@ USER
 sa
 !ok
 
+SELECT regexp_replace('abcabc', 'b') AS x;
+X
+acac
+!ok
+
+SELECT regexp_replace('abc def GHI', '[a-z]+', 'X') AS x;
+X
+X X GHI
+!ok
+
 # VERSION
 # Returns details about the currently installed release,
 # with specific Amazon Redshift version information at the end.
diff --git 
a/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java 
b/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java
index e20e4a82f2..55ef175c67 100644
--- a/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java
@@ -254,7 +254,15 @@ import static 
org.apache.calcite.sql.fun.SqlLibraryOperators.REGEXP_EXTRACT;
 import static 
org.apache.calcite.sql.fun.SqlLibraryOperators.REGEXP_EXTRACT_ALL;
 import static org.apache.calcite.sql.fun.SqlLibraryOperators.REGEXP_INSTR;
 import static org.apache.calcite.sql.fun.SqlLibraryOperators.REGEXP_LIKE;
-import static org.apache.calcite.sql.fun.SqlLibraryOperators.REGEXP_REPLACE;
+import static org.apache.calcite.sql.fun.SqlLibraryOperators.REGEXP_REPLACE_2;
+import static org.apache.calcite.sql.fun.SqlLibraryOperators.REGEXP_REPLACE_3;
+import static org.apache.calcite.sql.fun.SqlLibraryOperators.REGEXP_REPLACE_4;
+import static org.apache.calcite.sql.fun.SqlLibraryOperators.REGEXP_REPLACE_5;
+import static 
org.apache.calcite.sql.fun.SqlLibraryOperators.REGEXP_REPLACE_5_ORACLE;
+import static org.apache.calcite.sql.fun.SqlLibraryOperators.REGEXP_REPLACE_6;
+import static 
org.apache.calcite.sql.fun.SqlLibraryOperators.REGEXP_REPLACE_BIG_QUERY_3;
+import static 
org.apache.calcite.sql.fun.SqlLibraryOperators.REGEXP_REPLACE_PG_3;
+import static 
org.apache.calcite.sql.fun.SqlLibraryOperators.REGEXP_REPLACE_PG_4;
 import static org.apache.calcite.sql.fun.SqlLibraryOperators.REPEAT;
 import static org.apache.calcite.sql.fun.SqlLibraryOperators.REVERSE;
 import static org.apache.calcite.sql.fun.SqlLibraryOperators.REVERSE_SPARK;
@@ -864,7 +872,16 @@ public class RexImpTable {
           NotImplementor.of(insensitiveImplementor));
       map.put(NEGATED_POSIX_REGEX_CASE_SENSITIVE,
           NotImplementor.of(sensitiveImplementor));
-      map.put(REGEXP_REPLACE, new RegexpReplaceImplementor());
+      defineReflective(REGEXP_REPLACE_2, BuiltInMethod.REGEXP_REPLACE2.method);
+      defineReflective(REGEXP_REPLACE_3, BuiltInMethod.REGEXP_REPLACE3.method);
+      defineReflective(REGEXP_REPLACE_4, BuiltInMethod.REGEXP_REPLACE4.method);
+      defineReflective(REGEXP_REPLACE_5, 
BuiltInMethod.REGEXP_REPLACE5_OCCURRENCE.method,
+          BuiltInMethod.REGEXP_REPLACE5_MATCHTYPE.method);
+      defineReflective(REGEXP_REPLACE_5_ORACLE, 
BuiltInMethod.REGEXP_REPLACE5_OCCURRENCE.method);
+      defineReflective(REGEXP_REPLACE_6, BuiltInMethod.REGEXP_REPLACE6.method);
+      defineReflective(REGEXP_REPLACE_BIG_QUERY_3, 
BuiltInMethod.REGEXP_REPLACE_BIG_QUERY_3.method);
+      defineReflective(REGEXP_REPLACE_PG_3, 
BuiltInMethod.REGEXP_REPLACE_PG_3.method);
+      defineReflective(REGEXP_REPLACE_PG_4, 
BuiltInMethod.REGEXP_REPLACE_PG_4.method);
 
 
       // Multisets & arrays
@@ -2512,37 +2529,6 @@ public class RexImpTable {
     }
   }
 
-  /** Implementor for the {@code REGEXP_REPLACE} function. */
-  private static class RegexpReplaceImplementor extends 
AbstractRexCallImplementor {
-    RegexpReplaceImplementor() {
-      super("regexp_replace", NullPolicy.STRICT, false);
-    }
-
-    @Override Expression implementSafe(final RexToLixTranslator translator,
-        final RexCall call, final List<Expression> argValueList) {
-      // Boolean indicating if dialect uses default $-based indexing for
-      // regex capturing group (false means double-backslash-based indexing)
-      final boolean dollarIndexed =
-          translator.conformance.isRegexReplaceCaptureGroupDollarIndexed();
-
-      // Standard REGEXP_REPLACE implementation for default indexing.
-      if (dollarIndexed) {
-        final ReflectiveImplementor implementor =
-            new ReflectiveImplementor(
-                ImmutableList.of(BuiltInMethod.REGEXP_REPLACE3.method,
-                    BuiltInMethod.REGEXP_REPLACE4.method,
-                    BuiltInMethod.REGEXP_REPLACE5.method,
-                    BuiltInMethod.REGEXP_REPLACE6.method));
-        return implementor.implementSafe(translator, call, argValueList);
-      }
-
-      // Custom regexp replace method to preprocess double-backslashes into 
$-based indices.
-      return 
Expressions.call(Expressions.new_(SqlFunctions.RegexFunction.class),
-          "regexpReplaceNonDollarIndexed",
-          argValueList);
-    }
-  }
-
   /** Implementor for the {@code MONTHNAME} and {@code DAYNAME} functions.
    * Each takes a {@link java.util.Locale} argument. */
   private static class PeriodNameImplementor extends 
AbstractRexCallImplementor {
diff --git a/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java 
b/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
index 65bc1a0142..ff51fc6b66 100644
--- a/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
+++ b/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
@@ -632,6 +632,11 @@ public class SqlFunctions {
       return matchIndex;
     }
 
+    /** SQL {@code REGEXP_REPLACE} function with 2 arguments. */
+    public String regexpReplace(String s, String regex) {
+      return regexpReplace(s, regex, "", 1, 0, null);
+    }
+
     /** SQL {@code REGEXP_REPLACE} function with 3 arguments. */
     public String regexpReplace(String s, String regex,
         String replacement) {
@@ -644,12 +649,18 @@ public class SqlFunctions {
       return regexpReplace(s, regex, replacement, pos, 0, null);
     }
 
-    /** SQL {@code REGEXP_REPLACE} function with 5 arguments. */
+    /** SQL {@code REGEXP_REPLACE} function with 5 arguments. Last argument is 
occurrence. */
     public String regexpReplace(String s, String regex, String replacement,
         int pos, int occurrence) {
       return regexpReplace(s, regex, replacement, pos, occurrence, null);
     }
 
+    /** SQL {@code REGEXP_REPLACE} function with 5 arguments. Last argument is 
match type */
+    public String regexpReplace(String s, String regex, String replacement,
+        int pos, String matchType) {
+      return regexpReplace(s, regex, replacement, pos, 0, matchType);
+    }
+
     /** SQL {@code REGEXP_REPLACE} function with 6 arguments. */
     public String regexpReplace(String s, String regex, String replacement,
         int pos, int occurrence, @Nullable String matchType) {
@@ -663,6 +674,18 @@ public class SqlFunctions {
       return Unsafe.regexpReplace(s, pattern, replacement, pos, occurrence);
     }
 
+    /** SQL {@code REGEXP_REPLACE} function for PostgreSQL with 3 arguments. */
+    public String regexpReplacePg(String s, String regex, String replacement) {
+      return regexpReplace(s, regex, replacement, 1, 1, null);
+    }
+
+    /** SQL {@code REGEXP_REPLACE} function for PostgreSQL with 4 arguments. */
+    public String regexpReplacePg(String s, String regex, String replacement, 
String matchType) {
+      // Translate g flag to occurrence
+      final int occurrence = matchType.contains("g") ? 0 : 1;
+      return regexpReplace(s, regex, replacement, 1, occurrence, matchType);
+    }
+
     /** SQL {@code REGEXP_REPLACE} function with 3 arguments with
      * {@code \\} based indexing for capturing groups. */
     public String regexpReplaceNonDollarIndexed(String s, String regex,
@@ -706,6 +729,10 @@ public class SqlFunctions {
           // for consistency.
           flags &= ~Pattern.DOTALL;
           break;
+        case 'g':
+          // This flag is in PostgreSQL but doesn't apply to other libraries. 
Skip here since
+          // this is actually occurrence.
+          break;
         default:
           throw RESOURCE.invalidInputForRegexpReplace(stringFlags).ex();
         }
diff --git 
a/core/src/main/java/org/apache/calcite/sql/fun/SqlLibraryOperators.java 
b/core/src/main/java/org/apache/calcite/sql/fun/SqlLibraryOperators.java
index df3dcbd002..a3069cec11 100644
--- a/core/src/main/java/org/apache/calcite/sql/fun/SqlLibraryOperators.java
+++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlLibraryOperators.java
@@ -565,11 +565,107 @@ public abstract class SqlLibraryOperators {
           
OperandTypes.STRING_STRING_OPTIONAL_INTEGER_OPTIONAL_INTEGER_OPTIONAL_INTEGER,
           SqlFunctionCategory.STRING);
 
-  /** The "REGEXP_REPLACE(value, regexp, rep [, pos [, occurrence [, 
matchType]]])"
+  /** The "REGEXP_REPLACE(value, regexp)"
    * function. Replaces all substrings of value that match regexp with
    * {@code rep} and returns modified value. */
-  @LibraryOperator(libraries = {BIG_QUERY, MYSQL, ORACLE, REDSHIFT})
-  public static final SqlFunction REGEXP_REPLACE = new 
SqlRegexpReplaceFunction();
+  @LibraryOperator(libraries = {REDSHIFT})
+  public static final SqlFunction REGEXP_REPLACE_2 =
+      new SqlBasicFunction("REGEXP_REPLACE", SqlKind.OTHER_FUNCTION,
+          SqlSyntax.FUNCTION, true, ReturnTypes.VARCHAR_NULLABLE, null,
+          OperandHandlers.DEFAULT, OperandTypes.STRING_STRING, 0,
+          SqlFunctionCategory.STRING, call -> SqlMonotonicity.NOT_MONOTONIC, 
false) { };
+
+  /** The "REGEXP_REPLACE(value, regexp, rep)"
+   * function. Replaces all substrings of value that match regexp with
+   * {@code rep} and returns modified value. */
+  @LibraryOperator(libraries = {MYSQL, ORACLE, REDSHIFT})
+  public static final SqlFunction REGEXP_REPLACE_3 =
+      SqlBasicFunction.create("REGEXP_REPLACE", ReturnTypes.VARCHAR_NULLABLE,
+          OperandTypes.STRING_STRING_STRING, SqlFunctionCategory.STRING);
+
+  /** The "REGEXP_REPLACE(value, regexp, rep, pos)"
+   * function. Replaces all substrings of value that match regexp with
+   * {@code rep} and returns modified value. Start searching value from 
character position
+   * pos. */
+  @LibraryOperator(libraries = {MYSQL, ORACLE, REDSHIFT})
+  public static final SqlFunction REGEXP_REPLACE_4 =
+      new SqlBasicFunction("REGEXP_REPLACE", SqlKind.OTHER_FUNCTION,
+          SqlSyntax.FUNCTION, true, ReturnTypes.VARCHAR_NULLABLE, null,
+          OperandHandlers.DEFAULT, OperandTypes.family(SqlTypeFamily.STRING, 
SqlTypeFamily.STRING,
+              SqlTypeFamily.STRING, SqlTypeFamily.INTEGER),
+          0, SqlFunctionCategory.STRING, call -> 
SqlMonotonicity.NOT_MONOTONIC, false) { };
+
+  /** The "REGEXP_REPLACE(value, regexp, rep, pos, [ occurrence | matchType ])"
+   * function. Replaces all substrings of value that match regexp with
+   * {@code rep} and returns modified value. Start searching value from 
character position
+   * pos. Replace only the occurrence match or all matches if occurrence is 0. 
matchType
+   * is a string of flags to apply to the search. */
+  @LibraryOperator(libraries = {MYSQL, REDSHIFT})
+  public static final SqlFunction REGEXP_REPLACE_5 =
+      new SqlBasicFunction("REGEXP_REPLACE", SqlKind.OTHER_FUNCTION,
+          SqlSyntax.FUNCTION, true, ReturnTypes.VARCHAR_NULLABLE, null,
+          OperandHandlers.DEFAULT,
+          OperandTypes.or(
+              OperandTypes.family(SqlTypeFamily.STRING, SqlTypeFamily.STRING,
+                  SqlTypeFamily.STRING, SqlTypeFamily.INTEGER, 
SqlTypeFamily.INTEGER),
+              OperandTypes.family(SqlTypeFamily.STRING, SqlTypeFamily.STRING,
+                  SqlTypeFamily.STRING, SqlTypeFamily.INTEGER, 
SqlTypeFamily.STRING)),
+          0, SqlFunctionCategory.STRING, call -> 
SqlMonotonicity.NOT_MONOTONIC, false) { };
+
+  /** The "REGEXP_REPLACE(value, regexp, rep, pos, matchType)"
+   * function. Replaces all substrings of value that match regexp with
+   * {@code rep} and returns modified value. Start searching value from 
character position
+   * pos. Replace only the occurrence match or all matches if occurrence is 0. 
*/
+  @LibraryOperator(libraries = {ORACLE})
+  public static final SqlFunction REGEXP_REPLACE_5_ORACLE =
+      new SqlBasicFunction("REGEXP_REPLACE", SqlKind.OTHER_FUNCTION,
+          SqlSyntax.FUNCTION, true, ReturnTypes.VARCHAR_NULLABLE, null,
+          OperandHandlers.DEFAULT, OperandTypes.family(SqlTypeFamily.STRING, 
SqlTypeFamily.STRING,
+          SqlTypeFamily.STRING, SqlTypeFamily.INTEGER, SqlTypeFamily.INTEGER),
+          0, SqlFunctionCategory.STRING, call -> 
SqlMonotonicity.NOT_MONOTONIC, false) { };
+
+  /** The "REGEXP_REPLACE(value, regexp, rep, pos, occurrence, matchType)"
+   * function. Replaces all substrings of value that match regexp with
+   * {@code rep} and returns modified value. Start searching value from 
character position
+   * pos. Replace only the occurrence match or all matches if occurrence is 0. 
matchType
+   * is a string of flags to apply to the search. */
+  @LibraryOperator(libraries = {MYSQL, ORACLE, REDSHIFT})
+  public static final SqlFunction REGEXP_REPLACE_6 =
+      new SqlBasicFunction("REGEXP_REPLACE", SqlKind.OTHER_FUNCTION,
+          SqlSyntax.FUNCTION, true, ReturnTypes.VARCHAR_NULLABLE, null,
+          OperandHandlers.DEFAULT, OperandTypes.family(SqlTypeFamily.STRING, 
SqlTypeFamily.STRING,
+          SqlTypeFamily.STRING, SqlTypeFamily.INTEGER, SqlTypeFamily.INTEGER, 
SqlTypeFamily.STRING),
+          0, SqlFunctionCategory.STRING, call -> 
SqlMonotonicity.NOT_MONOTONIC, false) { };
+
+  /** The "REGEXP_REPLACE(value, regexp, rep)"
+   * function. Replaces all substrings of value that match regexp with
+   * {@code rep} and returns modified value. */
+  @LibraryOperator(libraries = {BIG_QUERY})
+  public static final SqlFunction REGEXP_REPLACE_BIG_QUERY_3 =
+      new SqlBasicFunction("REGEXP_REPLACE", SqlKind.OTHER_FUNCTION,
+          SqlSyntax.FUNCTION, true, ReturnTypes.VARCHAR_NULLABLE, null,
+          OperandHandlers.DEFAULT, OperandTypes.STRING_STRING_STRING, 0,
+          SqlFunctionCategory.STRING, call -> SqlMonotonicity.NOT_MONOTONIC, 
false) { };
+
+  /** The "REGEXP_REPLACE(value, regexp, rep)"
+   * function. Replaces all substrings of value that match regexp with
+   * {@code rep} and returns modified value. */
+  @LibraryOperator(libraries = {POSTGRESQL}, exceptLibraries = REDSHIFT)
+  public static final SqlFunction REGEXP_REPLACE_PG_3 =
+      new SqlBasicFunction("REGEXP_REPLACE", SqlKind.OTHER_FUNCTION,
+          SqlSyntax.FUNCTION, true, ReturnTypes.VARCHAR_NULLABLE, null,
+          OperandHandlers.DEFAULT, OperandTypes.STRING_STRING_STRING, 0,
+          SqlFunctionCategory.STRING, call -> SqlMonotonicity.NOT_MONOTONIC, 
false) { };
+
+  /** The "REGEXP_REPLACE(value, regexp, rep, flags)"
+   * function. Replaces all substrings of value that match regexp with
+   * {@code rep} and returns modified value. flags are applied to the search. 
*/
+  @LibraryOperator(libraries = {POSTGRESQL}, exceptLibraries = REDSHIFT)
+  public static final SqlFunction REGEXP_REPLACE_PG_4 =
+      new SqlBasicFunction("REGEXP_REPLACE", SqlKind.OTHER_FUNCTION,
+          SqlSyntax.FUNCTION, true, ReturnTypes.VARCHAR_NULLABLE, null,
+          OperandHandlers.DEFAULT, OperandTypes.STRING_STRING_STRING_STRING, 0,
+          SqlFunctionCategory.STRING, call -> SqlMonotonicity.NOT_MONOTONIC, 
false) { };
 
   /** The "REGEXP_SUBSTR(value, regexp[, position[, occurrence]])" function.
    * Returns the substring in value that matches the regexp. Returns NULL if 
there is no match. */
diff --git 
a/core/src/main/java/org/apache/calcite/sql/fun/SqlRegexpReplaceFunction.java 
b/core/src/main/java/org/apache/calcite/sql/fun/SqlRegexpReplaceFunction.java
deleted file mode 100644
index e56e74e2ec..0000000000
--- 
a/core/src/main/java/org/apache/calcite/sql/fun/SqlRegexpReplaceFunction.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * 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.calcite.sql.fun;
-
-import org.apache.calcite.sql.SqlCallBinding;
-import org.apache.calcite.sql.SqlFunction;
-import org.apache.calcite.sql.SqlFunctionCategory;
-import org.apache.calcite.sql.SqlKind;
-import org.apache.calcite.sql.SqlOperandCountRange;
-import org.apache.calcite.sql.type.OperandTypes;
-import org.apache.calcite.sql.type.ReturnTypes;
-import org.apache.calcite.sql.type.SqlOperandCountRanges;
-import org.apache.calcite.sql.type.SqlTypeFamily;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * The REGEXP_REPLACE(source_string, pattern, replacement [, pos, occurrence, 
match_type])
- * searches for a regular expression pattern and replaces every occurrence of 
the pattern
- * with the specified string.
- * */
-public class SqlRegexpReplaceFunction extends SqlFunction {
-
-  public SqlRegexpReplaceFunction() {
-    super("REGEXP_REPLACE", SqlKind.OTHER_FUNCTION,
-        ReturnTypes.VARCHAR_NULLABLE,
-        null, null, SqlFunctionCategory.STRING);
-  }
-
-  @Override public SqlOperandCountRange getOperandCountRange() {
-    return SqlOperandCountRanges.between(3, 6);
-  }
-
-  @Override public boolean checkOperandTypes(SqlCallBinding callBinding,
-      boolean throwOnFailure) {
-    final int operandCount = callBinding.getOperandCount();
-    assert operandCount >= 3;
-    if (operandCount == 3) {
-      return OperandTypes.STRING_STRING_STRING
-          .checkOperandTypes(callBinding, throwOnFailure);
-    }
-    final List<SqlTypeFamily> families = new ArrayList<>();
-    families.add(SqlTypeFamily.STRING);
-    families.add(SqlTypeFamily.STRING);
-    families.add(SqlTypeFamily.STRING);
-    for (int i = 3; i < operandCount; i++) {
-      if (i == 3) {
-        families.add(SqlTypeFamily.INTEGER);
-      }
-      if (i == 4) {
-        families.add(SqlTypeFamily.INTEGER);
-      }
-      if (i == 5) {
-        families.add(SqlTypeFamily.STRING);
-      }
-    }
-    return OperandTypes.family(families.toArray(new SqlTypeFamily[0]))
-        .checkOperandTypes(callBinding, throwOnFailure);
-  }
-}
diff --git a/core/src/main/java/org/apache/calcite/sql/type/OperandTypes.java 
b/core/src/main/java/org/apache/calcite/sql/type/OperandTypes.java
index 1bdc66f420..5ba35b889b 100644
--- a/core/src/main/java/org/apache/calcite/sql/type/OperandTypes.java
+++ b/core/src/main/java/org/apache/calcite/sql/type/OperandTypes.java
@@ -465,6 +465,10 @@ public abstract class OperandTypes {
           // Second and Third operand both are optional (operand index 0, 1, 2)
           number -> number == 1 || number == 2);
 
+  public static final FamilyOperandTypeChecker STRING_STRING_STRING_STRING =
+      family(SqlTypeFamily.STRING, SqlTypeFamily.STRING, SqlTypeFamily.STRING,
+          SqlTypeFamily.STRING);
+
   public static final FamilyOperandTypeChecker STRING_NUMERIC_OPTIONAL_STRING =
       family(
           ImmutableList.of(SqlTypeFamily.STRING, SqlTypeFamily.NUMERIC,
diff --git 
a/core/src/main/java/org/apache/calcite/sql/validate/SqlAbstractConformance.java
 
b/core/src/main/java/org/apache/calcite/sql/validate/SqlAbstractConformance.java
index 14c2c73e35..3205a48b40 100644
--- 
a/core/src/main/java/org/apache/calcite/sql/validate/SqlAbstractConformance.java
+++ 
b/core/src/main/java/org/apache/calcite/sql/validate/SqlAbstractConformance.java
@@ -105,10 +105,6 @@ public abstract class SqlAbstractConformance implements 
SqlConformance {
     return SqlConformanceEnum.DEFAULT.isOffsetLimitAllowed();
   }
 
-  @Override public boolean isRegexReplaceCaptureGroupDollarIndexed() {
-    return 
SqlConformanceEnum.DEFAULT.isRegexReplaceCaptureGroupDollarIndexed();
-  }
-
   @Override public boolean isPercentRemainderAllowed() {
     return SqlConformanceEnum.DEFAULT.isPercentRemainderAllowed();
   }
diff --git 
a/core/src/main/java/org/apache/calcite/sql/validate/SqlConformance.java 
b/core/src/main/java/org/apache/calcite/sql/validate/SqlConformance.java
index 896db041e8..b8501f02f1 100644
--- a/core/src/main/java/org/apache/calcite/sql/validate/SqlConformance.java
+++ b/core/src/main/java/org/apache/calcite/sql/validate/SqlConformance.java
@@ -256,22 +256,6 @@ public interface SqlConformance {
    */
   boolean isMinusAllowed();
 
-  /**
-   * Whether this dialect uses {@code $} (dollar) for indexing capturing groups
-   * in the replacement string of regular expression functions such as
-   * {@code REGEXP_REPLACE}. If false, the dialect uses {@code \\} (backslash)
-   * for indexing capturing groups.
-   *
-   * <p>For example, {@code REGEXP_REPLACE("abc", "a(.)c", "X\\1")} in BigQuery
-   * is equivalent to {@code REGEXP_REPLACE("abc", "a(.)c", "X$1")} in MySQL;
-   * both produce the result "Xb".
-   *
-   * <p>Among the built-in conformance levels, false in
-   * {@link SqlConformanceEnum#BIG_QUERY};
-   * true otherwise.
-   */
-  boolean isRegexReplaceCaptureGroupDollarIndexed();
-
   /**
    * Whether {@code CROSS APPLY} and {@code OUTER APPLY} operators are allowed
    * in the parser.
diff --git 
a/core/src/main/java/org/apache/calcite/sql/validate/SqlConformanceEnum.java 
b/core/src/main/java/org/apache/calcite/sql/validate/SqlConformanceEnum.java
index 1b7bd1a811..a610f19f16 100644
--- a/core/src/main/java/org/apache/calcite/sql/validate/SqlConformanceEnum.java
+++ b/core/src/main/java/org/apache/calcite/sql/validate/SqlConformanceEnum.java
@@ -240,15 +240,6 @@ public enum SqlConformanceEnum implements SqlConformance {
     }
   }
 
-  @Override public boolean isRegexReplaceCaptureGroupDollarIndexed() {
-    switch (this) {
-    case BIG_QUERY:
-      return false;
-    default:
-      return true;
-    }
-  }
-
   @Override public boolean isPercentRemainderAllowed() {
     switch (this) {
     case BABEL:
diff --git 
a/core/src/main/java/org/apache/calcite/sql/validate/SqlDelegatingConformance.java
 
b/core/src/main/java/org/apache/calcite/sql/validate/SqlDelegatingConformance.java
index 7b60bb4445..c8e2f7cdd2 100644
--- 
a/core/src/main/java/org/apache/calcite/sql/validate/SqlDelegatingConformance.java
+++ 
b/core/src/main/java/org/apache/calcite/sql/validate/SqlDelegatingConformance.java
@@ -87,10 +87,6 @@ public class SqlDelegatingConformance implements 
SqlConformance {
     return delegate.isMinusAllowed();
   }
 
-  @Override public boolean isRegexReplaceCaptureGroupDollarIndexed() {
-    return delegate.isRegexReplaceCaptureGroupDollarIndexed();
-  }
-
   @Override public boolean isApplyAllowed() {
     return delegate.isApplyAllowed();
   }
diff --git a/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java 
b/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java
index 3dd78e8772..bc5ca1d61a 100644
--- a/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java
+++ b/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java
@@ -586,15 +586,25 @@ public enum BuiltInMethod {
       String.class, String.class, int.class, int.class, int.class),
   REGEXP_LIKE3(SqlFunctions.RegexFunction.class, "regexpLike",
       String.class, String.class, String.class),
+  REGEXP_REPLACE2(SqlFunctions.RegexFunction.class, "regexpReplace",
+      String.class, String.class),
   REGEXP_REPLACE3(SqlFunctions.RegexFunction.class, "regexpReplace",
       String.class, String.class, String.class),
   REGEXP_REPLACE4(SqlFunctions.RegexFunction.class, "regexpReplace",
       String.class, String.class, String.class, int.class),
-  REGEXP_REPLACE5(SqlFunctions.RegexFunction.class, "regexpReplace",
+  REGEXP_REPLACE5_OCCURRENCE(SqlFunctions.RegexFunction.class, "regexpReplace",
       String.class, String.class, String.class, int.class, int.class),
+  REGEXP_REPLACE5_MATCHTYPE(SqlFunctions.RegexFunction.class, "regexpReplace",
+      String.class, String.class, String.class, int.class, String.class),
   REGEXP_REPLACE6(SqlFunctions.RegexFunction.class, "regexpReplace",
       String.class, String.class, String.class, int.class, int.class,
       String.class),
+  REGEXP_REPLACE_BIG_QUERY_3(SqlFunctions.RegexFunction.class, 
"regexpReplaceNonDollarIndexed",
+      String.class, String.class, String.class),
+  REGEXP_REPLACE_PG_3(SqlFunctions.RegexFunction.class, "regexpReplacePg",
+      String.class, String.class, String.class),
+  REGEXP_REPLACE_PG_4(SqlFunctions.RegexFunction.class, "regexpReplacePg",
+      String.class, String.class, String.class, String.class),
   IS_TRUE(SqlFunctions.class, "isTrue", Boolean.class),
   IS_NOT_FALSE(SqlFunctions.class, "isNotFalse", Boolean.class),
   NOT(SqlFunctions.class, "not", Boolean.class),
diff --git 
a/core/src/main/java/org/apache/calcite/util/RelToSqlConverterUtil.java 
b/core/src/main/java/org/apache/calcite/util/RelToSqlConverterUtil.java
index 36816a2bfa..6b510e4141 100644
--- a/core/src/main/java/org/apache/calcite/util/RelToSqlConverterUtil.java
+++ b/core/src/main/java/org/apache/calcite/util/RelToSqlConverterUtil.java
@@ -26,7 +26,7 @@ import org.apache.calcite.sql.SqlWriter;
 import org.apache.calcite.sql.fun.SqlTrimFunction;
 import org.apache.calcite.sql.parser.SqlParserPos;
 
-import static org.apache.calcite.sql.fun.SqlLibraryOperators.REGEXP_REPLACE;
+import static org.apache.calcite.sql.fun.SqlLibraryOperators.REGEXP_REPLACE_3;
 
 import static java.util.Objects.requireNonNull;
 
@@ -58,7 +58,7 @@ public abstract class RelToSqlConverterUtil {
       final SqlCharStringLiteral blankLiteral =
           SqlLiteral.createCharString("", call.getParserPosition());
       final SqlNode[] trimOperands = new SqlNode[] { call.operand(2), 
regexNode, blankLiteral };
-      final SqlCall regexReplaceCall = 
REGEXP_REPLACE.createCall(SqlParserPos.ZERO, trimOperands);
+      final SqlCall regexReplaceCall = 
REGEXP_REPLACE_3.createCall(SqlParserPos.ZERO, trimOperands);
       regexReplaceCall.unparse(writer, leftPrec, rightPrec);
     }
   }
diff --git a/core/src/test/java/org/apache/calcite/test/SqlFunctionsTest.java 
b/core/src/test/java/org/apache/calcite/test/SqlFunctionsTest.java
index c2dab83304..0f9f9049ed 100644
--- a/core/src/test/java/org/apache/calcite/test/SqlFunctionsTest.java
+++ b/core/src/test/java/org/apache/calcite/test/SqlFunctionsTest.java
@@ -495,6 +495,7 @@ class SqlFunctionsTest {
 
   @Test void testRegexpReplace() {
     final SqlFunctions.RegexFunction f = new SqlFunctions.RegexFunction();
+    assertThat(f.regexpReplace("abc", "b"), is("ac"));
     assertThat(f.regexpReplace("a b c", "b", "X"), is("a X c"));
     assertThat(f.regexpReplace("abc def ghi", "[g-z]+", "X"), is("abc def X"));
     assertThat(f.regexpReplace("abc def ghi", "[a-z]+", "X"), is("X X X"));
@@ -513,6 +514,11 @@ class SqlFunctionsTest {
         is("abc def GHI"));
     assertThat(f.regexpReplace("abc def GHI", "[a-z]+", "X", 1, 3, "i"),
         is("abc def X"));
+    assertThat(f.regexpReplacePg("abc def GHI", "[a-z]+", "X"), is("X def 
GHI"));
+    assertThat(f.regexpReplacePg("abc def GHI", "[a-z]+", "X", "g"),
+        is("X X GHI"));
+    assertThat(f.regexpReplacePg("ABC def GHI", "[a-z]+", "X", "i"),
+        is("X def GHI"));
 
     try {
       f.regexpReplace("abc def ghi", "[a-z]+", "X", 0);
diff --git a/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java 
b/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
index 927274d21c..2ff73fb2ed 100644
--- a/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
+++ b/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
@@ -11946,6 +11946,33 @@ public class SqlValidatorTest extends 
SqlValidatorTestCase {
         .columnType("VARCHAR NOT NULL");
   }
 
+  @Test void testPgRegexpReplace() {
+    final SqlOperatorTable opTable = operatorTableFor(SqlLibrary.POSTGRESQL);
+
+    expr("REGEXP_REPLACE('a b c', 'a', 'X')")
+        .withOperatorTable(opTable)
+        .columnType("VARCHAR NOT NULL");
+    expr("REGEXP_REPLACE('abc def ghi', '[a-z]+', 'X')")
+        .withOperatorTable(opTable)
+        .columnType("VARCHAR NOT NULL");
+    expr("REGEXP_REPLACE('abc def ghi', '[a-z]+', 'X')")
+        .withOperatorTable(opTable)
+        .columnType("VARCHAR NOT NULL");
+    expr("REGEXP_REPLACE('abc def GHI', '[a-z]+', 'X', 'c')")
+        .withOperatorTable(opTable)
+        .columnType("VARCHAR NOT NULL");
+    // Implicit type coercion.
+    expr("REGEXP_REPLACE(null, '(-)', '###')")
+        .withOperatorTable(opTable)
+        .columnType("VARCHAR");
+    expr("REGEXP_REPLACE('100-200', null, '###')")
+        .withOperatorTable(opTable)
+        .columnType("VARCHAR");
+    expr("REGEXP_REPLACE('100-200', '(-)', null)")
+        .withOperatorTable(opTable)
+        .columnType("VARCHAR");
+  }
+
   @Test void testInvalidFunctionCall() {
     final SqlOperatorTable operatorTable =
         MockSqlOperatorTable.standard().extend();
diff --git a/site/_docs/reference.md b/site/_docs/reference.md
index 180c3aab51..b3565bc1fa 100644
--- a/site/_docs/reference.md
+++ b/site/_docs/reference.md
@@ -2854,7 +2854,9 @@ In the following:
 | b | REGEXP_EXTRACT_ALL(string, regexp)             | Returns an array of all 
substrings in *string* that matches the *regexp*. Returns an empty array if 
there is no match
 | b | REGEXP_INSTR(string, regexp [, position [, occurrence [, 
occurrence_position]]]) | Returns the lowest 1-based position of the substring 
in *string* that matches the *regexp*, starting search at *position* (default 
1), and until locating the nth *occurrence* (default 1). Setting 
occurrence_position (default 0) to 1 returns the end position of substring + 1. 
Returns 0 if there is no match
 | m o p r s | REGEXP_LIKE(string, regexp [, flags])  | Equivalent to `string1 
RLIKE string2` with an optional parameter for search flags. Supported flags 
are: <ul><li>i: case-insensitive matching</li><li>c: case-sensitive 
matching</li><li>n: newline-sensitive matching</li><li>s: non-newline-sensitive 
matching</li><li>m: multi-line</li></ul>
+| r | REGEXP_REPLACE(string, regexp)                 | Replaces all substrings 
of *string* that match *regexp* with the empty string
 | b m o r | REGEXP_REPLACE(string, regexp, rep [, pos [, occurrence [, 
matchType]]]) | Replaces all substrings of *string* that match *regexp* with 
*rep* at the starting *pos* in expr (if omitted, the default is 1), 
*occurrence* specifies which occurrence of a match to search for (if omitted, 
the default is 1), *matchType* specifies how to perform matching
+| p | REGEXP_REPLACE(string, regexp, rep [, matchType]) | Replaces substrings 
of *string* that match *regexp* with *rep* at the starting *pos* in expr, 
*matchType* specifies how to perform matching and whether to only replace first 
match or all
 | b | REGEXP_SUBSTR(string, regexp [, position [, occurrence]]) | Synonym for 
REGEXP_EXTRACT
 | b m p r s | REPEAT(string, integer)                | Returns a string 
consisting of *string* repeated of *integer* times; returns an empty string if 
*integer* is less than 1
 | b m | REVERSE(string)                              | Returns *string* with 
the order of the characters reversed
diff --git a/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java 
b/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java
index 4427dcd83f..7e880e94c6 100644
--- a/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java
+++ b/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java
@@ -5829,10 +5829,32 @@ public class SqlOperatorTest {
     f.checkQuery("select regexp_instr('a9cadca5c4aecghi', 'a[0-9]c', 1, 3)");
   }
 
-  @Test void testRegexpReplaceFunc() {
+  @Test void testRegexpReplace2Func() {
     final SqlOperatorFixture f0 = fixture();
     final Consumer<SqlOperatorFixture> consumer = f -> {
-      f.setFor(SqlLibraryOperators.REGEXP_REPLACE);
+      f.setFor(SqlLibraryOperators.REGEXP_REPLACE_2);
+
+      // Tests for regexp replace generic functionality
+      f.checkString("regexp_replace('a b c', 'b')", "a  c",
+          "VARCHAR NOT NULL");
+      f.checkString("regexp_replace('abc1 def2 ghi3', '[a-z]+')", "1 2 3",
+          "VARCHAR NOT NULL");
+      f.checkString("regexp_replace('100-200', '(\\d+)')", "-",
+          "VARCHAR NOT NULL");
+      f.checkString("regexp_replace('100-200', '(-)')", "100200",
+          "VARCHAR NOT NULL");
+
+      f.checkQuery("select regexp_replace('a b c', 'b')");
+    };
+    final List<SqlLibrary> libraries =
+        list(SqlLibrary.REDSHIFT);
+    f0.forEachLibrary(libraries, consumer);
+  }
+
+  @Test void testRegexpReplace3Func() {
+    final SqlOperatorFixture f0 = fixture();
+    final Consumer<SqlOperatorFixture> consumer = f -> {
+      f.setFor(SqlLibraryOperators.REGEXP_REPLACE_3);
 
       // Tests for regexp replace generic functionality
       f.checkString("regexp_replace('a b c', 'b', 'X')", "a X c",
@@ -5846,16 +5868,6 @@ public class SqlOperatorTest {
       f.checkNull("regexp_replace(cast(null as varchar), '(-)', '###')");
       f.checkNull("regexp_replace('100-200', cast(null as varchar), '###')");
       f.checkNull("regexp_replace('100-200', '(-)', cast(null as varchar))");
-      f.checkString("regexp_replace('abc def ghi', '[a-z]+', 'X', 2)", "aX X 
X",
-          "VARCHAR NOT NULL");
-      f.checkString("regexp_replace('abc def ghi', '[a-z]+', 'X', 1, 3)", "abc 
def X",
-          "VARCHAR NOT NULL");
-      f.checkString("regexp_replace('abc def GHI', '[a-z]+', 'X', 1, 3, 'c')", 
"abc def GHI",
-          "VARCHAR NOT NULL");
-      f.checkString("regexp_replace('abc def GHI', '[a-z]+', 'X', 1, 3, 'i')", 
"abc def X",
-          "VARCHAR NOT NULL");
-      f.checkString("regexp_replace('abc def GHI', '[a-z]+', 'X', 1, 3, 'i')", 
"abc def X",
-          "VARCHAR NOT NULL");
       f.checkString("regexp_replace('abc\t\ndef\t\nghi', '\t', '+')", 
"abc+\ndef+\nghi",
           "VARCHAR NOT NULL");
       f.checkString("regexp_replace('abc\t\ndef\t\nghi', '\t\n', '+')", 
"abc+def+ghi",
@@ -5864,38 +5876,141 @@ public class SqlOperatorTest {
           "VARCHAR NOT NULL");
 
       f.checkQuery("select regexp_replace('a b c', 'b', 'X')");
+    };
+    final List<SqlLibrary> libraries =
+        list(SqlLibrary.BIG_QUERY, SqlLibrary.MYSQL, SqlLibrary.ORACLE, 
SqlLibrary.REDSHIFT);
+    f0.forEachLibrary(libraries, consumer);
+
+    // Tests to verify double-backslashes are ignored for indexing in other 
dialects
+    final SqlOperatorFixture f2 =
+        
f0.withLibrary(SqlLibrary.MYSQL).withConformance(SqlConformanceEnum.MYSQL_5);
+    f2.checkString("regexp_replace('abc16', 'b(.*)(\\d)', '\\\\2\\\\1X')", 
"a\\2\\1X",
+        "VARCHAR NOT NULL");
+    f2.checkString("regexp_replace('abcdefghijabc', 'abc(.)', '\\\\-11x')", 
"\\-11xefghijabc",
+        "VARCHAR NOT NULL");
+    f2.checkString("regexp_replace('abcdefghijabc', 'abc(.)', '$1x')", 
"dxefghijabc",
+        "VARCHAR NOT NULL");
+  }
+
+  @Test void testRegexpReplace4Func() {
+    final SqlOperatorFixture f0 = fixture();
+    final Consumer<SqlOperatorFixture> consumer = f -> {
+      f.setFor(SqlLibraryOperators.REGEXP_REPLACE_4);
+
+      // Tests for regexp replace generic functionality
+      f.checkString("regexp_replace('abc def ghi', '[a-z]+', 'X', 2)", "aX X 
X",
+          "VARCHAR NOT NULL");
       f.checkQuery("select regexp_replace('a b c', 'b', 'X', 1)");
+    };
+    final List<SqlLibrary> libraries =
+        list(SqlLibrary.MYSQL, SqlLibrary.ORACLE, SqlLibrary.REDSHIFT);
+    f0.forEachLibrary(libraries, consumer);
+  }
+
+  @Test void testRegexpReplace5Func() {
+    final SqlOperatorFixture f0 = fixture();
+    final Consumer<SqlOperatorFixture> consumer = f -> {
+      f.setFor(SqlLibraryOperators.REGEXP_REPLACE_5);
+
+      // Tests for regexp replace generic functionality
+      f.checkString("regexp_replace('abc def ghi', '[a-z]+', 'X', 1, 3)", "abc 
def X",
+          "VARCHAR NOT NULL");
+      f.checkString("regexp_replace('ABC def ghi', '[a-z]+', 'X', 1, 'i')", "X 
X X",
+          "VARCHAR NOT NULL");
       f.checkQuery("select regexp_replace('a b c', 'b', 'X', 1, 3)");
+      f.checkQuery("select regexp_replace('a b c', 'b', 'X', 1, 'i')");
+    };
+    final List<SqlLibrary> libraries =
+        list(SqlLibrary.MYSQL, SqlLibrary.REDSHIFT);
+    f0.forEachLibrary(libraries, consumer);
+  }
+
+  @Test void testRegexpReplace5OracleFunc() {
+    final SqlOperatorFixture f = fixture().withLibrary(SqlLibrary.ORACLE);
+    f.setFor(SqlLibraryOperators.REGEXP_REPLACE_5_ORACLE);
+
+    // Tests for regexp replace generic functionality
+    f.checkString("regexp_replace('abc def ghi', '[a-z]+', 'X', 1, 3)", "abc 
def X",
+        "VARCHAR NOT NULL");
+    f.checkQuery("select regexp_replace('a b c', 'b', 'X', 1, 1)");
+  }
+
+  @Test void testRegexpReplace6Func() {
+    final SqlOperatorFixture f0 = fixture();
+    final Consumer<SqlOperatorFixture> consumer = f -> {
+      f.setFor(SqlLibraryOperators.REGEXP_REPLACE_6);
+
+      // Tests for regexp replace generic functionality
+      f.checkString("regexp_replace('abc def GHI', '[a-z]+', 'X', 1, 3, 'c')", 
"abc def GHI",
+          "VARCHAR NOT NULL");
+      f.checkString("regexp_replace('abc def GHI', '[a-z]+', 'X', 1, 3, 'i')", 
"abc def X",
+          "VARCHAR NOT NULL");
+
       f.checkQuery("select regexp_replace('a b c', 'b', 'X', 1, 3, 'i')");
     };
     final List<SqlLibrary> libraries =
-        list(SqlLibrary.BIG_QUERY, SqlLibrary.MYSQL, SqlLibrary.ORACLE,
-            SqlLibrary.REDSHIFT);
+        list(SqlLibrary.MYSQL, SqlLibrary.ORACLE, SqlLibrary.REDSHIFT);
     f0.forEachLibrary(libraries, consumer);
+  }
+
+  @Test void testRegexpReplaceBigQuery3Func() {
+    final SqlOperatorFixture f = 
fixture().setFor(SqlLibraryOperators.REGEXP_REPLACE_BIG_QUERY_3)
+        .withLibrary(SqlLibrary.BIG_QUERY);
 
     // Tests for double-backslash indexed capturing groups for regexp_replace 
in BQ
-    final SqlOperatorFixture f1 =
-        
f0.withLibrary(SqlLibrary.BIG_QUERY).withConformance(SqlConformanceEnum.BIG_QUERY);
-    f1.checkString("regexp_replace('abc16', 'b(.*)(\\d)', '\\\\2\\\\1X')", 
"a6c1X",
+    f.checkString("regexp_replace('abc16', 'b(.*)(\\d)', '\\\\2\\\\1X')", 
"a6c1X",
         "VARCHAR NOT NULL");
-    f1.checkString("regexp_replace('a\\bc56a\\bc37', 'b(.)(\\d)', 
'\\\\2\\\\0X')",
+    f.checkString("regexp_replace('a\\bc56a\\bc37', 'b(.)(\\d)', 
'\\\\2\\\\0X')",
         "a\\5bc5X6a\\3bc3X7", "VARCHAR NOT NULL");
-    f1.checkString("regexp_replace('abcdefghijabc', 'abc(.)', 
'\\\\\\\\123xyz')",
+    f.checkString("regexp_replace('abcdefghijabc', 'abc(.)', 
'\\\\\\\\123xyz')",
         "\\123xyzefghijabc", "VARCHAR NOT NULL");
-    f1.checkString("regexp_replace('abcdefghijabc', 'abc(.)', '$1xy')",
+    f.checkString("regexp_replace('abcdefghijabc', 'abc(.)', '$1xy')",
         "$1xyefghijabc", "VARCHAR NOT NULL");
-    f1.checkString("regexp_replace('abc123', 'b(.*)(\\d)', '\\\\\\\\$ 
$\\\\\\\\')",
+    f.checkString("regexp_replace('abc123', 'b(.*)(\\d)', '\\\\\\\\$ 
$\\\\\\\\')",
         "a\\$ $\\", "VARCHAR NOT NULL");
 
-    // Tests to verify double-backslashes are ignored for indexing in other 
dialects
-    final SqlOperatorFixture f2 =
-        
f0.withLibrary(SqlLibrary.MYSQL).withConformance(SqlConformanceEnum.MYSQL_5);
-    f2.checkString("regexp_replace('abc16', 'b(.*)(\\d)', '\\\\2\\\\1X')", 
"a\\2\\1X",
+    f.checkQuery("select regexp_replace('a b c', 'b', 'X')");
+  }
+
+  @Test void testRegexpReplacePg3Func() {
+    final SqlOperatorFixture f = 
fixture().setFor(SqlLibraryOperators.REGEXP_REPLACE_PG_3)
+        .withLibrary(SqlLibrary.POSTGRESQL);
+
+    // Tests for regexp replace generic functionality
+    f.checkString("regexp_replace('a b c', 'b', 'X')", "a X c",
         "VARCHAR NOT NULL");
-    f2.checkString("regexp_replace('abcdefghijabc', 'abc(.)', '\\\\-11x')", 
"\\-11xefghijabc",
+    f.checkString("regexp_replace('abc def ghi', '[a-z]+', 'X')", "X def ghi",
         "VARCHAR NOT NULL");
-    f2.checkString("regexp_replace('abcdefghijabc', 'abc(.)', '$1x')", 
"dxefghijabc",
+    f.checkString("regexp_replace('100-200', '(\\d+)', 'num')", "num-200",
         "VARCHAR NOT NULL");
+    f.checkString("regexp_replace('100-200', '(-)', '###')", "100###200",
+        "VARCHAR NOT NULL");
+    f.checkNull("regexp_replace(cast(null as varchar), '(-)', '###')");
+    f.checkNull("regexp_replace('100-200', cast(null as varchar), '###')");
+    f.checkNull("regexp_replace('100-200', '(-)', cast(null as varchar))");
+    f.checkString("regexp_replace('abc\t\ndef\t\nghi', '\t', '+')", 
"abc+\ndef\t\nghi",
+        "VARCHAR NOT NULL");
+    f.checkString("regexp_replace('abc\t\ndef\t\nghi', '\t\n', '+')", 
"abc+def\t\nghi",
+        "VARCHAR NOT NULL");
+    f.checkString("regexp_replace('abc\t\ndef\t\nghi', '\\w+', '+')", 
"+\t\ndef\t\nghi",
+        "VARCHAR NOT NULL");
+
+    f.checkQuery("select regexp_replace('a b c', 'b', 'X')");
+  }
+
+  @Test void testRegexpReplacePg4Func() {
+    final SqlOperatorFixture f = 
fixture().setFor(SqlLibraryOperators.REGEXP_REPLACE_PG_4)
+        .withLibrary(SqlLibrary.POSTGRESQL);
+
+    // Tests for regexp replace generic functionality
+    f.checkString("regexp_replace('abc def GHI', '[a-z]+', 'X', 'c')", "X def 
GHI",
+        "VARCHAR NOT NULL");
+    f.checkString("regexp_replace('ABC def GHI', '[a-z]+', 'X', 'i')", "X def 
GHI",
+        "VARCHAR NOT NULL");
+    f.checkString("regexp_replace('abc def ghi', '[a-z]+', 'X', 'g')", "X X X",
+        "VARCHAR NOT NULL");
+
+    f.checkQuery("select regexp_replace('a b c', 'b', 'X', 'i')");
   }
 
   @Test void testRegexpExtractFunc() {


Reply via email to