[CALCITE-6314] Add RANDOM function (enabled in Postgres library)
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 1bc88f1..9cabfcb 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
@@ -232,6 +232,7 @@
 import static org.apache.calcite.sql.fun.SqlLibraryOperators.PARSE_TIMESTAMP;
 import static org.apache.calcite.sql.fun.SqlLibraryOperators.PARSE_URL;
 import static org.apache.calcite.sql.fun.SqlLibraryOperators.POW;
+import static org.apache.calcite.sql.fun.SqlLibraryOperators.RANDOM;
 import static org.apache.calcite.sql.fun.SqlLibraryOperators.REGEXP;
 import static org.apache.calcite.sql.fun.SqlLibraryOperators.REGEXP_CONTAINS;
 import static org.apache.calcite.sql.fun.SqlLibraryOperators.REGEXP_EXTRACT;
@@ -652,6 +653,7 @@
           BuiltInMethod.RAND_SEED.method);
       defineReflective(RAND_INTEGER, BuiltInMethod.RAND_INTEGER.method,
           BuiltInMethod.RAND_INTEGER_SEED.method);
+      defineReflective(RANDOM, BuiltInMethod.RAND.method);
 
       defineMethod(ACOS, BuiltInMethod.ACOS.method, NullPolicy.STRICT);
       defineMethod(ACOSH, BuiltInMethod.ACOSH.method, NullPolicy.STRICT);
diff --git a/core/src/main/java/org/apache/calcite/sql/SqlBasicFunction.java b/core/src/main/java/org/apache/calcite/sql/SqlBasicFunction.java
index 7e3da75..f2f54a6 100644
--- a/core/src/main/java/org/apache/calcite/sql/SqlBasicFunction.java
+++ b/core/src/main/java/org/apache/calcite/sql/SqlBasicFunction.java
@@ -48,6 +48,7 @@
   private final SqlOperandHandler operandHandler;
   private final int callValidator;
   private final Function<SqlOperatorBinding, SqlMonotonicity> monotonicityInference;
+  private final boolean dynamic;
 
   //~ Constructors -----------------------------------------------------------
 
@@ -73,7 +74,8 @@
       SqlOperandTypeChecker operandTypeChecker,
       Integer callValidator,
       SqlFunctionCategory category,
-      Function<SqlOperatorBinding, SqlMonotonicity> monotonicityInference) {
+      Function<SqlOperatorBinding, SqlMonotonicity> monotonicityInference,
+      boolean dynamic) {
     super(name, kind,
         requireNonNull(returnTypeInference, "returnTypeInference"),
         operandTypeInference,
@@ -84,6 +86,7 @@
     this.callValidator = requireNonNull(callValidator, "callValidator");
     this.monotonicityInference =
         requireNonNull(monotonicityInference, "monotonicityInference");
+    this.dynamic = dynamic;
   }
 
   /** Creates a {@code SqlBasicFunction} whose name is the same as its kind
@@ -94,7 +97,7 @@
     return new SqlBasicFunction(kind.name(), kind,
         SqlSyntax.FUNCTION, true, returnTypeInference, null,
         OperandHandlers.DEFAULT, operandTypeChecker, 0,
-        SqlFunctionCategory.SYSTEM, call -> SqlMonotonicity.NOT_MONOTONIC);
+        SqlFunctionCategory.SYSTEM, call -> SqlMonotonicity.NOT_MONOTONIC, false);
   }
 
   /** Creates a {@code SqlBasicFunction}
@@ -106,7 +109,7 @@
     return new SqlBasicFunction(name, SqlKind.OTHER_FUNCTION,
         SqlSyntax.FUNCTION, true, returnTypeInference, null,
         OperandHandlers.DEFAULT, operandTypeChecker, 0,
-        SqlFunctionCategory.NUMERIC, call -> SqlMonotonicity.NOT_MONOTONIC);
+        SqlFunctionCategory.NUMERIC, call -> SqlMonotonicity.NOT_MONOTONIC, false);
   }
 
   /** Creates a {@code SqlBasicFunction}
@@ -117,7 +120,7 @@
     return new SqlBasicFunction(name, SqlKind.OTHER_FUNCTION,
         SqlSyntax.FUNCTION, true, returnTypeInference, null,
         OperandHandlers.DEFAULT, operandTypeChecker, 0,
-        category, call -> SqlMonotonicity.NOT_MONOTONIC);
+        category, call -> SqlMonotonicity.NOT_MONOTONIC, false);
   }
 
   //~ Methods ----------------------------------------------------------------
@@ -151,12 +154,16 @@
     super.validateCall(call, validator, scope, operandScope);
   }
 
+  @Override public boolean isDynamicFunction() {
+    return dynamic;
+  }
+
   /** Returns a copy of this function with a given name. */
   public SqlBasicFunction withName(String name) {
     return new SqlBasicFunction(name, kind, syntax, deterministic,
         getReturnTypeInference(), getOperandTypeInference(), operandHandler,
         getOperandTypeChecker(), callValidator,
-        getFunctionType(), monotonicityInference);
+        getFunctionType(), monotonicityInference, dynamic);
   }
 
   /** Returns a copy of this function with a given kind. */
@@ -164,14 +171,14 @@
     return new SqlBasicFunction(getName(), kind, syntax, deterministic,
         getReturnTypeInference(), getOperandTypeInference(), operandHandler,
         getOperandTypeChecker(), callValidator,
-        getFunctionType(), monotonicityInference);
+        getFunctionType(), monotonicityInference, dynamic);
   }
 
   /** Returns a copy of this function with a given category. */
   public SqlBasicFunction withFunctionType(SqlFunctionCategory category) {
     return new SqlBasicFunction(getName(), kind, syntax, deterministic,
         getReturnTypeInference(), getOperandTypeInference(), operandHandler,
-        getOperandTypeChecker(), callValidator, category, monotonicityInference);
+        getOperandTypeChecker(), callValidator, category, monotonicityInference, dynamic);
   }
 
   /** Returns a copy of this function with a given syntax. */
@@ -179,7 +186,7 @@
     return new SqlBasicFunction(getName(), kind, syntax, deterministic,
         getReturnTypeInference(), getOperandTypeInference(), operandHandler,
         getOperandTypeChecker(), callValidator,
-        getFunctionType(), monotonicityInference);
+        getFunctionType(), monotonicityInference, dynamic);
   }
 
   /** Returns a copy of this function with a given strategy for inferring
@@ -189,7 +196,7 @@
     return new SqlBasicFunction(getName(), kind, syntax, deterministic,
         returnTypeInference, getOperandTypeInference(), operandHandler,
         getOperandTypeChecker(), callValidator,
-        getFunctionType(), monotonicityInference);
+        getFunctionType(), monotonicityInference, dynamic);
   }
 
   /** Returns a copy of this function with a given strategy for inferring
@@ -199,7 +206,7 @@
     return new SqlBasicFunction(getName(), kind, syntax, deterministic,
         getReturnTypeInference(), operandTypeInference, operandHandler,
         getOperandTypeChecker(), callValidator,
-        getFunctionType(), monotonicityInference);
+        getFunctionType(), monotonicityInference, dynamic);
   }
 
   /** Returns a copy of this function with a given strategy for handling
@@ -208,14 +215,14 @@
     return new SqlBasicFunction(getName(), kind, syntax, deterministic,
         getReturnTypeInference(), getOperandTypeInference(), operandHandler,
         getOperandTypeChecker(), callValidator,
-        getFunctionType(), monotonicityInference);
+        getFunctionType(), monotonicityInference, dynamic);
   }
   /** Returns a copy of this function with a given determinism. */
   public SqlBasicFunction withDeterministic(boolean deterministic) {
     return new SqlBasicFunction(getName(), kind, syntax, deterministic,
         getReturnTypeInference(), getOperandTypeInference(), operandHandler,
         getOperandTypeChecker(), callValidator,
-        getFunctionType(), monotonicityInference);
+        getFunctionType(), monotonicityInference, dynamic);
   }
 
   /** Returns a copy of this function with a given strategy for inferring
@@ -225,13 +232,27 @@
     return new SqlBasicFunction(getName(), kind, syntax, deterministic,
         getReturnTypeInference(), getOperandTypeInference(), operandHandler,
         getOperandTypeChecker(), callValidator,
-        getFunctionType(), monotonicityInference);
+        getFunctionType(), monotonicityInference, dynamic);
   }
 
-  public SqlFunction withValidation(int callValidator) {
+  public SqlBasicFunction withValidation(int callValidator) {
     return new SqlBasicFunction(getName(), kind, syntax, deterministic,
         getReturnTypeInference(), getOperandTypeInference(), operandHandler,
         getOperandTypeChecker(), callValidator,
-        getFunctionType(), monotonicityInference);
+        getFunctionType(), monotonicityInference, dynamic);
+  }
+
+  public SqlBasicFunction withDynamic(boolean dynamic) {
+    return new SqlBasicFunction(getName(), kind, syntax, deterministic,
+        getReturnTypeInference(), getOperandTypeInference(), operandHandler,
+        getOperandTypeChecker(), callValidator,
+        getFunctionType(), monotonicityInference, dynamic);
+  }
+
+  public SqlBasicFunction withOperandTypeChecker(SqlOperandTypeChecker operandTypeChecker) {
+    return new SqlBasicFunction(getName(), kind, syntax, deterministic,
+        getReturnTypeInference(), getOperandTypeInference(), operandHandler,
+        operandTypeChecker, callValidator,
+        getFunctionType(), monotonicityInference, dynamic);
   }
 }
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 0c700d9..b32658f 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
@@ -2311,4 +2311,10 @@
   @LibraryOperator(libraries = {SPARK})
   public static final SqlFunction GETBIT =
       BIT_GET.withName("GETBIT");
+
+  /** The RANDOM() function. Equivalent to RAND(). */
+  @LibraryOperator(libraries = {POSTGRESQL})
+  public static final SqlFunction RANDOM = SqlStdOperatorTable.RAND
+      .withName("RANDOM")
+      .withOperandTypeChecker(OperandTypes.NILADIC);
 }
diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlRandFunction.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlRandFunction.java
deleted file mode 100644
index 096d6f9..0000000
--- a/core/src/main/java/org/apache/calcite/sql/fun/SqlRandFunction.java
+++ /dev/null
@@ -1,52 +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.SqlFunction;
-import org.apache.calcite.sql.SqlFunctionCategory;
-import org.apache.calcite.sql.SqlKind;
-import org.apache.calcite.sql.type.OperandTypes;
-import org.apache.calcite.sql.type.ReturnTypes;
-
-/**
- * The <code>RAND</code> function. There are two overloads:
- *
- * <ul>
- *   <li>RAND() returns a random double between 0 and 1
- *   <li>RAND(seed) returns a random double between 0 and 1, initializing the
- *   random number generator with seed on first call
- * </ul>
- */
-public class SqlRandFunction extends SqlFunction {
-  //~ Constructors -----------------------------------------------------------
-
-  public SqlRandFunction() {
-    super("RAND",
-        SqlKind.OTHER_FUNCTION,
-        ReturnTypes.DOUBLE,
-        null,
-        OperandTypes.NILADIC.or(OperandTypes.NUMERIC),
-        SqlFunctionCategory.NUMERIC);
-  }
-
-  //~ Methods ----------------------------------------------------------------
-
-  // Plans referencing context variables should never be cached
-  @Override public boolean isDynamicFunction() {
-    return true;
-  }
-}
diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlStdOperatorTable.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlStdOperatorTable.java
index bf40fb2..93f5105 100644
--- a/core/src/main/java/org/apache/calcite/sql/fun/SqlStdOperatorTable.java
+++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlStdOperatorTable.java
@@ -317,7 +317,10 @@
 
   /** The {@code RAND([seed])} function, which yields a random double,
    * optionally with seed. */
-  public static final SqlRandFunction RAND = new SqlRandFunction();
+  public static final SqlBasicFunction RAND = SqlBasicFunction
+      .create("RAND", ReturnTypes.DOUBLE,
+          OperandTypes.NILADIC.or(OperandTypes.NUMERIC), SqlFunctionCategory.NUMERIC)
+      .withDynamic(true);
 
   /**
    * Internal integer arithmetic division operator, '<code>/INT</code>'. This
diff --git a/site/_docs/reference.md b/site/_docs/reference.md
index d8712e5..542b1f6 100644
--- a/site/_docs/reference.md
+++ b/site/_docs/reference.md
@@ -2815,6 +2815,7 @@
 | b | PARSE_TIMESTAMP(format, string[, timeZone])    | Uses format specified by *format* to convert *string* representation of timestamp to a TIMESTAMP WITH LOCAL TIME ZONE value in *timeZone*
 | h s | PARSE_URL(urlString, partToExtract [, keyToExtract] ) | Returns the specified *partToExtract* from the *urlString*. Valid values for *partToExtract* include HOST, PATH, QUERY, REF, PROTOCOL, AUTHORITY, FILE, and USERINFO. *keyToExtract* specifies which query to extract
 | b s | POW(numeric1, numeric2)                      | Returns *numeric1* raised to the power *numeric2*
+| p | RANDOM()                                       | Generates a random double between 0 and 1 inclusive
 | s | REGEXP(string, regexp)                         | Equivalent to `string1 RLIKE string2`
 | b | REGEXP_CONTAINS(string, regexp)                | Returns whether *string* is a partial match for the *regexp*
 | b | REGEXP_EXTRACT(string, regexp [, position [, occurrence]]) | Returns the substring in *string* that matches the *regexp*, starting search at *position* (default 1), and until locating the nth *occurrence* (default 1). Returns NULL if there is no match
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 01f308a..5a772c8 100644
--- a/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java
+++ b/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java
@@ -6359,6 +6359,22 @@
     }
   }
 
+  /** Test case for
+   * <a href="https://issues.apache.org/jira/browse/CALCITE-6314">[CALCITE-6314]
+   * Add RANDOM function (enabled in Postgres library)</a>. */
+  @Test void testRandomFunc() {
+    final SqlOperatorFixture f = fixture();
+    f.setFor(SqlLibraryOperators.RANDOM, VmName.EXPAND);
+    f.checkFails("^random^", "Column 'RANDOM' not found in any table", false);
+    Consumer<SqlOperatorFixture> consumer = fixture -> {
+      for (int i = 0; i < 100; i++) {
+        // Result must always be between 0 and 1, inclusive.
+        fixture.checkScalarApprox("random()", "DOUBLE NOT NULL", isWithin(0.5, 0.5));
+      }
+    };
+    f.forEachLibrary(list(SqlLibrary.POSTGRESQL), consumer);
+  }
+
   @Test void testRandIntegerSeedFunc() {
     final SqlOperatorFixture f = fixture();
     f.setFor(SqlStdOperatorTable.RAND_INTEGER, VmName.EXPAND);